GraphQL API
We are now ready to expose the new comments functionality in our core package through an API. You'll recall that we use GraphQL for our API in our starter.
info
We use GraphQL for our API in the create sst
starter.
You don't need to know a lot about GraphQL upfront, this tutorial can help with that.
What is GraphQL
To start, let's get a quick overview of GraphQL and how we use it in our create sst
setup.
GraphQL is a query language for your API that provides more structure than open ended REST APIs.
Learn GraphQL
If you're interested in learning GraphQL, we recommend the tutorial in their docs.
Why GraphQL
One of the biggest benefits of using GraphQL is that it can effectively separate your frontend and backend. In the REST API world, if you needed to make a change to the frontend and display some data differently. Or display different data, you needed to think about making a change to the APIs as well.
In the case of GraphQL, you describe all your data, the relationships, and the actions you can carry out on that data once. The frontend can request whatever it needs in a way that makes sense for it.
These benefits become all the more valuable when you have multiple clients. For example, imagine your desktop site shows all the articles along with their comments, while the mobile site only shows the articles. This is easy to do with GraphQL. Each client just specifies what it needs.
GraphQL also has a huge community that has built really great tooling around it for things like code-generation, authorization, logging, and much more.
Code-first GraphQL
In the create sst
setup we do what's called Code-first GraphQL. This means that you write all of your GraphQL API definitions in TypeScript, instead of splitting them across GraphQL files and TS files.
info
The create sst
setup uses an approach called Code-first GraphQL.
We use a library called Pothos to do this. And while it's not the default way to do GraphQL, it's a fantastically productive pattern and we recommend it for everyone. Here are some of the reasons why:
Single source of truth
Separating your schema from your resolver code usually requires code-generation to keep things in sync. This is a clunky workflow and can feel tedious to maintain things in two places.
With Pothos you have a single source of truth for both the schema and the implementation. You only have to write the definitions once in TypeScript for your entire application; backend and frontend.
First class TypeScript support
Pothos is a TypeScript-first library. This means the entire API has been designed to maximize typesafety and inference. You will get useful autocomplete, full typesafety, while having to write very few manual type annotations.
Plugin system
Pothos comes with a set of incredibly useful plugins. These simplify implementing common patterns like paging, authorization, and more.
In the next chapter we'll look at an example of this and add our new feature to the GraphQL schema, in code.
Lambda optimized GraphQL
The GraphQL setup in our starter ships with a Lambda optimized GraphQL server. We've taken care to make sure it's as small as possible to minimize cold starts; while still including what you'll need to ship complete GraphQL APIs.
Behind the scenes
Let's take a look at how this is all wired up.
First, as we talked about in Project Structure chapter, our GraphQL API is defined in
stacks/Api.ts
.routes: {
"POST /graphql": {
type: "graphql",
function: {
handler: "packages/functions/src/graphql/graphql.handler",
},
pothos: {
schema: "packages/functions/src/graphql/schema.ts",
output: "packages/graphql/schema.graphql",
commands: [
"cd packages/graphql && npx @genql/cli --output ./genql --schema ./schema.graphql --esm",
],
},
},
},Our API has a single route at
/graphql
. It's anApiPothosRouteProps
.Let's look at what we are configuring here:
- The
handler
points to where the Lambda function is. - The
schema
is the reference to a GraphQL schema. More on this in a second. - The
output
is where Pothos outputs the GraphQL schema to a file. By writing to a file, we are able to use other tools in the GraphQL ecosystem. - Finally, the
commands
let you specify any scripts you want to run after the schema has been generated. We'll look at what we are running below.
- The
The GraphQL schema is specified in
packages/functions/src/graphql/schema.ts
.services/functions/graphql/schema.tsimport { builder } from "./builder";
import "./types/article";
export const schema = builder.toSchema({});It's doing two things:
Get the Pothos
SchemaBuilder
that we define inpackages/functions/src/graphql/builder.ts
.packages/functions/src/graphql/builder.tsimport SchemaBuilder from "@pothos/core";
export const builder = new SchemaBuilder({});
builder.queryType({});
builder.mutationType({});This creates a new instance that we'll use to build out our GraphQL schema.
Import all our GraphQL schema types. Right now we only have the one for our article,
./types/article
. These use thebuilder
from above to build out our schema. We'll look at this in detail in the next chapter.Finally, get the GraphQL schema from Pothos by running
builder.toSchema()
.
We then pass the GraphQL schema into the Lambda optimized GraphQL handler,
GraphQLHandler
, that we talked about above.It's defined in
packages/functions/src/graphql/graphql.ts
.packages/functions/src/graphql/graphql.tsimport { schema } from "./schema";
import { GraphQLHandler } from "sst/node/graphql";
export const handler = GraphQLHandler({
schema,
});Finally, we are running a script after our schema has been generated.
npx genql --output ./graphql/genql --schema ./graphql/schema.graphql --esm
We are using Genql, to generate a typed GraphQL client to the
--output
directory. It uses the GraphQL schema that Pothos generates in the--schema
directory. We'll be using this later in our frontend React app.We internally have a watcher that regenerates the typed frontend client when we make changes to our Pothos schema. So the pipeline looks like:
- Detect changes in the Pothos schema.
- Generate a standard GraphQL schema.
- Generate our typed frontend GraphQL client from the schema.
Now let's expose the comments feature with our GraphQL API.