Skip to content

Commit

Permalink
Provide people with tabs so they can use classes as well (#4284)
Browse files Browse the repository at this point in the history
This provides people with the option to choose between the template
approach or the classes approach. This is a proposal to tackle
#1368

[Preview](https://graphql-7w0ort26u-the-graph-ql-foundation.vercel.app/)

This has been applied throughout the codebase now, however one of the
things I am uncertain about is how we offer `buildSchema` with the
GraphQLDefer/... directives? Should we add an option to `buildSchema`?
The exports defined in that chapter seem to only exist in v17 so we
should explicitly flag that.
  • Loading branch information
JoviDeCroock authored Nov 7, 2024
1 parent e9b6b62 commit 577a9ad
Show file tree
Hide file tree
Showing 9 changed files with 818 additions and 85 deletions.
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@
/denoDist
/websiteDist
/website/out
/website/**/*.mdx
.next
60 changes: 54 additions & 6 deletions website/pages/authentication-and-express-middleware.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,67 @@ title: Authentication and Express Middleware
sidebarTitle: Authentication & Middleware
---

import { Tabs } from 'nextra/components';

It's simple to use any Express middleware in conjunction with `graphql-http`. In particular, this is a great pattern for handling authentication.

To use middleware with a GraphQL resolver, just use the middleware like you would with a normal Express app. The `request` object is then available as the second argument in any resolver.

For example, let's say we wanted our server to log the IP address of every request, and we also want to write an API that returns the IP address of the caller. We can do the former with middleware, and the latter by accessing the `request` object in a resolver. Here's server code that implements this:

<Tabs items={['SDL', 'Code']}>
<Tabs.Tab>
```js
const express = require('express');
const { createHandler } = require('graphql-http/lib/use/express');
const { buildSchema } = require('graphql');

const schema = buildSchema(`
type Query {
ip: String
}
`);
const schema = buildSchema(`type Query { ip: String }`);

function loggingMiddleware(req, res, next) {
console.log('ip:', req.ip);
next();
}

const root = {
ip(args, context) {
return context.ip;
},
};

const app = express();
app.use(loggingMiddleware);
app.all(
'/graphql',
createHandler({
schema: schema,
rootValue: root,
context: (req) => ({
ip: req.raw.ip,
}),
}),
);
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');

````
</Tabs.Tab>
<Tabs.Tab>
```js
const express = require('express');
const { createHandler } = require('graphql-http/lib/use/express');
const {
GraphQLObjectType,
GraphQLSchema,
GraphQLString,
} = require('graphql');
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: { ip: { type: GraphQLString } },
}),
});
function loggingMiddleware(req, res, next) {
console.log('ip:', req.ip);
Expand All @@ -45,7 +90,10 @@ app.all(
);
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');
```
````
</Tabs.Tab>
</Tabs>
In a REST API, authentication is often handled with a header, that contains an auth token which proves what user is making this request. Express middleware processes these headers and puts authentication data on the Express `request` object. Some middleware modules that handle authentication like this are [Passport](http://passportjs.org/), [express-jwt](https://github.com/auth0/express-jwt), and [express-session](https://github.com/expressjs/session). Each of these modules works with `graphql-http`.
Expand Down
61 changes: 60 additions & 1 deletion website/pages/basic-types.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
title: Basic Types
---

import { Tabs } from 'nextra/components';

In most situations, all you need to do is to specify the types for your API using the GraphQL schema language, taken as an argument to the `buildSchema` function.

The GraphQL schema language supports the scalar types of `String`, `Int`, `Float`, `Boolean`, and `ID`, so you can use these directly in the schema you pass to `buildSchema`.
Expand All @@ -12,6 +14,8 @@ To use a list type, surround the type in square brackets, so `[Int]` is a list o

Each of these types maps straightforwardly to JavaScript, so you can just return plain old JavaScript objects in APIs that return these types. Here's an example that shows how to use some of these basic types:

<Tabs items={['SDL', 'Code']}>
<Tabs.Tab>
```js
const express = require('express');
const { createHandler } = require('graphql-http/lib/use/express');
Expand All @@ -26,6 +30,56 @@ const schema = buildSchema(`
}
`);

// The root provides a resolver function for each API endpoint
const root = {
quoteOfTheDay() {
return Math.random() < 0.5 ? 'Take it easy' : 'Salvation lies within';
},
random() {
return Math.random();
},
rollThreeDice() {
return [1, 2, 3].map((\_) => 1 + Math.floor(Math.random() \* 6));
},
};

const app = express();
app.all(
'/graphql',
createHandler({
schema: schema,
rootValue: root,
}),
);
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');

````
</Tabs.Tab>
<Tabs.Tab>
```js
const express = require('express');
const { createHandler } = require('graphql-http/lib/use/express');
const {
GraphQLObjectType,
GraphQLSchema,
GraphQLString,
GraphQLFloat,
GraphQLList,
} = require('graphql');
// Construct a schema
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
quoteOfTheDay: { type: GraphQLString },
random: { type: GraphQLFloat },
rollThreeDice: { type: new GraphQLList(GraphQLFloat) },
},
}),
});
// The root provides a resolver function for each API endpoint
const root = {
quoteOfTheDay() {
Expand All @@ -40,16 +94,21 @@ const root = {
};
const app = express();
app.all(
'/graphql',
createHandler({
schema: schema,
rootValue: root,
}),
);
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');
```
````
</Tabs.Tab>
</Tabs>
If you run this code with `node server.js` and browse to http://localhost:4000/graphql you can try out these APIs.
Expand Down
35 changes: 21 additions & 14 deletions website/pages/constructing-types.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,30 @@
title: Constructing Types
---

import { Tabs } from 'nextra/components';

For many apps, you can define a fixed schema when the application starts, and define it using GraphQL schema language. In some cases, it's useful to construct a schema programmatically. You can do this using the `GraphQLSchema` constructor.

When you are using the `GraphQLSchema` constructor to create a schema, instead of defining `Query` and `Mutation` types solely using schema language, you create them as separate object types.

For example, let's say we are building a simple API that lets you fetch user data for a few hardcoded users based on an id. Using `buildSchema` we could write a server with:

<Tabs items={['SDL', 'Code']}>
<Tabs.Tab>
```js
const express = require('express');
const { createHandler } = require('graphql-http/lib/use/express');
const { buildSchema } = require('graphql');

const schema = buildSchema(`
type User {
id: String
name: String
}
type Query {
user(id: String): User
}
type User {
id: String
name: String
}
type Query {
user(id: String): User
}
`);

// Maps id to User object
Expand Down Expand Up @@ -52,10 +56,10 @@ app.all(
);
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');
```

We can implement this same API without using GraphQL schema language:

````
</Tabs.Tab>
<Tabs.Tab>
```js
const express = require('express');
const { createHandler } = require('graphql-http/lib/use/express');
Expand Down Expand Up @@ -110,8 +114,11 @@ app.all(
);
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');
```
````
</Tabs.Tab>
</Tabs>
When we use this method of creating the API, the root level resolvers are implemented on the `Query` and `Mutation` types rather than on a `root` object.
When we use the `GraphQLSchema` constructor method of creating the API, the root level resolvers are implemented on the `Query` and `Mutation` types rather than on a `root` object.
This is particularly useful if you want to create a GraphQL schema automatically from something else, like a database schema. You might have a common format for something like creating and updating database records. This is also useful for implementing features like union types which don't map cleanly to ES6 classes and schema language.
This can be particularly useful if you want to create a GraphQL schema automatically from something else, like a database schema. You might have a common format for something like creating and updating database records. This is also useful for implementing features like union types which don't map cleanly to ES6 classes and schema language.
54 changes: 45 additions & 9 deletions website/pages/getting-started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ title: Getting Started With GraphQL.js
sidebarTitle: Getting Started
---

import { Tabs } from 'nextra/components';

{/* title can be removed in Nextra 4, since sidebar title will take from first h1 */}

# Getting Started With GraphQL.js
Expand All @@ -19,7 +21,7 @@ and arrow functions, so if you aren't familiar with them you might want to read
To create a new project and install GraphQL.js in your current directory:

```bash
```sh npm2yarn
npm init
npm install graphql --save
```
Expand All @@ -28,18 +30,49 @@ npm install graphql --save

To handle GraphQL queries, we need a schema that defines the `Query` type, and we need an API root with a function called a “resolver” for each API endpoint. For an API that just returns “Hello world!”, we can put this code in a file named `server.js`:

<Tabs items={['SDL', 'Code']}>
<Tabs.Tab>
```javascript
let { graphql, buildSchema } = require('graphql');
const { graphql, buildSchema } = require('graphql');

// Construct a schema, using GraphQL schema language
let schema = buildSchema(`
type Query {
hello: String
}
`);
const schema = buildSchema(`type Query { hello: String } `);

// The rootValue provides a resolver function for each API endpoint
const rootValue = {
hello() {
return 'Hello world!';
},
};

// Run the GraphQL query '{ hello }' and print out the response
graphql({
schema,
source: '{ hello }',
rootValue,
}).then((response) => {
console.log(response);
});
});

````
</Tabs.Tab>
<Tabs.Tab>
```javascript
const { graphql, GraphQLSchema, GraphQLObjectType } = require('graphql');
// Construct a schema
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
hello: { type: GraphQLString },
},
}),
});
// The rootValue provides a resolver function for each API endpoint
let rootValue = {
const rootValue = {
hello() {
return 'Hello world!';
},
Expand All @@ -53,7 +86,10 @@ graphql({
}).then((response) => {
console.log(response);
});
```
````
</Tabs.Tab>
</Tabs>
If you run this with:
Expand Down
Loading

0 comments on commit 577a9ad

Please sign in to comment.