Installation of SveteKit and Apollo Server
To create a SvelteKit application, we can follow the steps in SvelteKit documentation. Because I use node adapters, don't forget to add node adapters to the SvelteKit application.
npm install --save-dev @sveltejs/adapter-node
then change the node adapter in the svelte.config.js
file.
import adapter from '@sveltejs/adapter-node';
.env settings
To set environment variables in a SvelteKit application, we can create .env
and .env.production
files in the root of the application folder. These files will be used to store environment variables that will be used in the SvelteKit application such as connection settings to the database, secret keys, etc. Meanwhile, we only need one environment variable, namely PROJECT_PATH
which will be used to store the path of the project during development and production.
Contents of the .env
file:
NODE_ENV=development
PROJECT_PATH=.
Contents of the .env.production
file:
PROJECT_PATH=.
Apollo Server Installation
After that, we can add Apollo Server into the SvelteKit application.
npm install @apollo/server @apollo/subgraph graphql graphql-tag
Create Apollo Server
Create Schema
First, we will create a GraphQL schema that will be used by Apollo Server. This schema will define the data types that will be used by the server. We will create a schema that contains only the Query
data type. This schema file will be saved in the src/schema.graphql
folder.
type Book {
title: Strings
author: String
}
type Query {
hello: String
books: [Book]
}
Create Resolvers
After creating the schema, we will create resolvers that will be used by Apollo Server. These resolvers will process queries sent by the SvelteKit application. These resolvers will be stored in the src/resolvers.ts
folder.
export default {
Query: {
hello: () => 'Hello World',
books: () => {
return [
{
title: 'The Awakening',
author: 'Kate Chopin',
},
{
title: 'City of Glass',
author: 'Paul Auster',
},
];
},
}
}
For the time being, we only create resolvers for Query
with two queries, namely hello
and books
. The data in books
is dummy data that we use for the example.
Create Apollo Server
After creating the schema and resolvers, we will create an Apollo Server that will be used by the SvelteKit application. This server will be accessible via the /graphql
endpoint. This server file will be stored in the src/routes/graphql/+server.ts
folder.
import { ApolloServer, HeaderMap, type BaseContext, type ContextFunction, type HTTPGraphQLRequest } from '@apollo/server';
import { readFileSync } from 'fs';
import resolvers from '../../resolvers.js';
import { parse } from 'url';
import { join } from 'path';
import { PROJECT_PATH } from '$env/static/private';
interface MyContextFunctionArgument {
req: Request;
res: Response;
}
interface HTTPGraphQLHead {
status?: number;
headers: HeaderMap;
}
type HTTPGraphQLResponseBody =
| { kind: 'complete'; string: string }
| { kind: 'chunked'; asyncIterator: AsyncIterableIterator<string> };
type HTTPGraphQLResponse = HTTPGraphQLHead & {
body: HTTPGraphQLResponseBody;
};
const defaultContext: ContextFunction<[MyContextFunctionArgument], any> = async () => ({});
const context: ContextFunction<[MyContextFunctionArgument], BaseContext> = defaultContext;
The initial code is some import
and interface
that will be used by Apollo Server. Ignore if you don't understand the code. Frankly, I don't understand either, I mostly copy-pasted from the Apollo Server documentation :D.
let path = join(PROJECT_PATH, 'src', 'schema.graphql');
if (process.env.NODE_ENV !== 'development') {
path = join(PROJECT_PATH, 'schema.graphql');
}
const typeDef = readFileSync(path, 'utf8');
const server = new ApolloServer({
typeDefs: typeDef,
resolvers,
csrfPrevention: false,
});
server.start().catch(err => console.error(err));
After that, we continue the code below by adding code to read the schema that we created previously. We use readFileSync
to read the schema file that we created. We also added csrfPrevention: false
to disable csrf prevention on Apollo Server. server.start()
is used to start Apollo Server.
export async function POST({ request }) {
const headers = new HeaderMap();
for (const [key, value] of Object.entries(request.headers)) {
if (value !== undefined) {
headers.set(key, Array.isArray(value) ? value.join(', ') : value);
}
}
const httpGraphQLRequest: HTTPGraphQLRequest = {
method: 'POST',
headers,
body: await request.json(),
search: parse(request.url).search ?? '',
};
const result = await server
.executeHTTPGraphQLRequest({
httpGraphQLRequest,
context: () => context({ req: request, res: new Response}), // Provide a valid Response object instead of null
});
if (result.body.kind === 'complete') {
// complete the response
return new Response(result.body.string, {
state: 200,
headers: { 'content-type': 'application/json', },
});
}
if (result.body.kind === 'chunked' && 'asyncIterator' in result.body) {
// chunked response
const chunkedBody = result.body as { asyncIterator: AsyncIterableIterator<any> };
return new Response(
new ReadableStream({
async start(controller) {
for await (const chunk of chunkedBody.asyncIterator) {
controller.enqueue(chunk);
}
controller.close();
},
}), {
state: 200,
headers: { 'content-type': 'application/json', },
},
);
}
}
The next code is the code to handle incoming requests to the Apollo Server. We use the POST
method to handle incoming requests. We use executeHTTPGraphQLRequest
to execute the query sent by the SvelteKit application. I just set the headers
and status
sections in Response
manually, because I was in a rush
Trials
To test whether Apollo Server is running properly, we can use a graphql client
application such as Insomnia
or Postman
. We can send hello
and books
queries to Apollo Server by accessing the endpoint http://localhost:5173/graphql
.
query {
hello
books {
title
author
}
}
If the Apollo Server is running well, then we will get a response from the query we sent.
Build and Deploy
For build, based on the code above, in section:
...
let path = join(PROJECT_PATH, 'src', 'schema.graphql');
if (process.env.NODE_ENV !== 'development') {
path = join(PROJECT_PATH, 'schema.graphql');
}
...
We need to copy the schema.graphql
file into the project root folder. Once the build is complete, the files are moved into the build
folder (default of Vite). In the meantime, we can use scripts
in package.json
to copy the file. For example:
{
...
"scripts": {
"copyWindows": "copy src\\schema.graphql . && vite build && move /y schema.graphql build\\schema.graphql",
"copyLinux": "cp src/schema.graphql . && vite build && mv schema.graphql build/schema.graphql",
...
}
...
}
copyWindows
for Windows and copyLinux
for Linux. We can use npm run copyWindows
or npm run copyLinux
to copy the schema.graphql
file into the project root folder, build the application, and move the schema.graphql
file into the build
folder.
Conclusion
The complete code is in this repository. Using Apollo Server, we can create a GraphQL server that will be used by SvelteKit applications. Apollo Server makes it easy for us to create a GraphQL server that can be used by SvelteKit applications.