nuxt-graphql-middleware 3.0.0-beta.2 → 3.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,27 +1,21 @@
1
- # Nuxt GraphQL Middleware
1
+ ![nuxt-graphql-middleware banner](docs/banner.png?raw=true "Nuxt GraphQL Middleware - Expose queries and mutations as fully typed API routes.")
2
2
 
3
- GraphQL in the backend, fetch in the frontend. With TypeScript support.
3
+ # Nuxt GraphQL Middleware
4
4
 
5
- ## Idea
6
- When using GraphQL you have to bundle your queries in your frontend build and
7
- send them with every request. If you have lots of queries and/or fragments,
8
- this can increase your frontend bundle size significantly. In addition you have
9
- to expose your entire GraphQL endpoint to the public (if you don't use persisted
10
- queries).
5
+ Expose GraphQL queries and mutations as fully typed API routes.
11
6
 
12
- This module aims to fix this by performing any GraphQL requests only on the
13
- server side. It passes the response to the frontend via a simple JSON endpoint.
14
- So you can have all the benefits of GraphQL but without any bloat.
7
+ **[Documentation](https://nuxt-graphql-middleware.dulnan.net)** **[npm](https://www.npmjs.com/package/nuxt-graphql-middleware)** **[Version 2.x (for Nuxt 2)](https://github.com/dulnan/nuxt-graphql-middleware/tree/2.x)**
15
8
 
16
- It optionally generates TypeScript type files of your schema, queries and
17
- mutations via [graphql-codegen](https://github.com/dotansimha/graphql-code-generator).
9
+ [![Test](https://github.com/dulnan/nuxt-graphql-middleware/actions/workflows/node.js.yml/badge.svg?branch=main)](https://github.com/dulnan/nuxt-graphql-middleware/actions/workflows/node.js.yml)
18
10
 
19
11
  ## Features
20
- - GraphQL queries and mutations using graphql-request
21
- - Client plugin to perform queries or mutations
22
- - Fully flexible: Modify request headers, responses or handle errors
23
- - HMR for queries and mutations
24
- - TypeScript integration for schema, queries and mutations
12
+ - Exposes each query and mutation as an API route
13
+ - GraphQL requests are only done on the server side
14
+ - No GraphQL documents in client bundle
15
+ - Includes composables to perform queries or mutations
16
+ - Modify request headers, responses and handle errors
17
+ - HMR for all GraphQL files
18
+ - Full TypeScript integration for schema, queries, mutations and fragments using [graphql-code-generator](https://github.com/dotansimha/graphql-code-generator)
25
19
 
26
20
  # Setup
27
21
 
@@ -32,333 +26,35 @@ npm install --save nuxt-graphql-middleware
32
26
 
33
27
  Minimal configuration needed:
34
28
  ```javascript
35
- module.exports = {
29
+ export default defineNuxtConfig({
36
30
  modules: ['nuxt-graphql-middleware'],
37
31
  graphqlMiddleware: {
38
- graphqlServer: 'http://example.com/graphql',
39
- typescript: {
40
- enabled: true
41
- },
42
- queries: {
43
- articles: '~/pages/query.articles.graphql',
44
- },
45
- plugin: {
46
- enabled: true
47
- }
32
+ graphqlEndpoint: 'https://example.com/graphql',
48
33
  }
49
- }
50
- ```
51
-
52
- # Usage
53
-
54
- ## With provided plugin
55
-
56
- ### Simple query
57
- ```javascript
58
- asyncData({ app }) {
59
- return app.$graphql.query('articles').then(data => {
60
- return { articles: data.articles }
61
- })
62
- }
63
- ```
64
-
65
- ### With variables
66
-
67
- Anything you provide in the second argument will be passed 1:1 as variables to
68
- the GraphQL request.
69
-
70
- ```javascript
71
- asyncData({ app }) {
72
- return app.$graphql.query('articles', { limit: 10 }).then(data => {
73
- return { articles: data.articles }
74
- })
75
- }
76
- ```
77
-
78
- ### Simple mutation
79
-
80
- Anything you provide in the second argument is used as the mutation input.
81
- ```javascript
82
- createPost(post) {
83
- return app.$graphql.mutate('createPost', post).then(response => {
84
- if (response.hasError) {
85
- this.errors.push(response.error)
86
- }
87
- })
88
- }
89
- ```
90
-
91
- ## Custom requests
92
-
93
- You can do your own requests without using the plugin.
94
- Query variables are passed as a JSON encoded string.
95
-
96
- ```javascript
97
- fetch('/__api/query?name=articles')
98
- fetch('/__api/query?name=articles&variables={"limit":10}')
99
- fetch('/__api/mutate?name=createPost', {
100
- method: 'POST',
101
- body: JSON.stringify(post)
102
34
  })
103
35
  ```
104
36
 
37
+ ## Usage
105
38
 
106
- # Configuration
107
-
108
- ## Options
109
-
110
- ### graphqlServer: string
111
- URL of your GraphQL server.
112
-
113
- ### endpointNamespace: string
114
-
115
- Namespace where the server middleware is running, e.g. '/__api'.
116
- => http://localhost:3000/__api/query
117
-
118
- ### debug: boolean
119
- Output additional info about available queries and mutations to the console.
120
-
121
- ### queries: Record<string, string>
122
- Map of query name => filePath.
123
-
124
- ### mutations: Record<string, string>
125
- Map of mutation name => filePath.
126
-
127
- ### outputPath: string
128
- If set, the module will write the compiled queries and mutations in this
129
- folder.
130
-
131
- ### plugin.enabled: boolean
132
- Enable the helper plugin.
133
-
134
- ### plugin.cacheInBrowser: boolean
135
- Cache requests in the plugin (on client side / browser).
136
-
137
- This enables a simple cache (using a Map) in the browser, which will cache up
138
- to 30 queries. This is useful to provide near instant rendering when going back
139
- and forth in the browser history.
140
-
141
- Queries are cached based on their full URL (incl. query string).
142
-
143
- ### plugin.cacheInServer: boolean
144
- Same as cacheInBrowser, but the queries are also cached server side.
145
- *Note:* There is no way to purge this cache! Only use this if you're fine with
146
- returning potentially outdated responses.
147
-
148
- ### server.middleware: (req: Request, res: Response, next: NextFunction) => any
149
- An express middleware. Can be used for example to add an authentication or CORS
150
- check.
151
-
152
- ```javascript
153
- function(req, res, next) {
154
- if (isLoggedIn(req.headers.cookie)) {
155
- return next()
156
- }
157
- res.status(403).send()
158
- }
159
- ```
160
-
161
- ### server.fetchOptions: Record<string, any>
162
- Object of options passed to the fetch request to GraphQL.
163
-
164
- ### server.buildHeaders: (req: Request, name: string, type: string) => Record<string, any>
165
- Called before every request
166
-
167
- ```javascript
168
- function (req, name, type) {
169
- if (isLoggedIn(req.headers.cookie)) {
170
- if (type === 'mutation') {
171
- return {
172
- Authorization: 'Basic ' + process.env.BASIC_AUTH_WRITE
173
- }
174
- }
175
- }
176
- }
177
- ```
178
-
179
- ### server.buildEndpoint: (req: Request) => string
180
- Called before every request. This allows you to set the URL for the GraphQL
181
- server.
182
-
183
- This is useful if you have multiple endpoints, for example with a language
184
- prefix.
185
-
186
- ```javascript
187
- function (req) {
188
- const language = getLanguageFromHeaders(req.headers)
189
- return `https://example.com/${language}/graphql`
190
- }
191
- ```
192
-
193
-
194
- ### server.onQueryResponse: (response: GraphQLResponse, req: Request, res: Response) => any
195
- Handle GraphQL server query responses before they are sent to the client.
196
-
197
- ```javascript
198
- function(response, req, res) {
199
- return res.json({
200
- data: response.data,
201
- time: Date.now()
202
- })
203
- }
204
- ```
205
-
206
- ### server.onQueryError: (error: ClientError, req: Request, res: Response) => any
207
- Handle GraphQL server query errors before they are sent to the client.
208
-
209
- ### server.onMutationResponse: (response: GraphQLResponse, req: Request, res: Response) => any
210
- Handle GraphQL server mutation responses before they are sent to the client.
211
-
212
- ### server.onMutationError: (error: ClientError, req: Request, res: Response) => any
213
- Handle GraphQL server mutation errors before they are sent to the client.
214
-
215
- ### typescript.enabled: boolean
216
- Enable TypeScript integration.
217
-
218
- ### typescript.schemaOutputPath: string
219
- Folder where the downloaded schema.graphql file is saved.
220
-
221
- ### typescript.skipSchemaDownload: boolean
222
- Don't download the schema. Use this for example if you commit the schema in
223
- your repository, so that it's available during deployment.
224
-
225
- ### typescript.schemaOptions: [UrlSchemaOptions](https://github.com/dotansimha/graphql-code-generator/blob/master/packages/utils/plugins-helpers/src/types.ts#L74)
226
- Options passed to graphql-codegen.
227
-
228
- ### typescript.typesOutputPath: string
229
- Folder where the generated graphql-schema.d.ts and graphql-operations.d.ts
230
- files are saved.
231
-
232
- ## Extend $graphql plugin
233
-
234
- If you want to add custom headers to the request made by `$graphql` to the
235
- middleware, create a plugin and add a `beforeRequest` method:
236
-
237
- ```javascript
238
- export default (pluginContext) => {
239
- pluginContext.$graphql.beforeRequest((ctx, options) => {
240
- options.headers['accept-language'] = ctx.route.params.lang
241
- return options
242
- })
243
- }
244
- ```
245
-
246
- You have access to the context via the first parameter. The second parameter
247
- provides the fetch options, which you have to return.
248
-
249
- It's also possible to return a Promise, useful if you need to handle things
250
- like a token refresh. Be aware that this method is called before every query or
251
- mutation request, so make sure it doesn't take too much time.
39
+ Write your first query, e.g. in pages/films.query.graphql:
252
40
 
253
-
254
- ### Integrate with nuxt-auth
255
-
256
- Add a `beforeRequest` method in a custom plugin:
257
-
258
- ```javascript
259
- export default (pluginContext) => {
260
- pluginContext.$graphql.beforeRequest((ctx, options) => {
261
- if (ctx.$auth.loggedIn) {
262
- options.headers['authorization'] = ctx.$auth.strategy.token.get()
263
- }
264
- return options
265
- })
266
- }
267
- ```
268
-
269
- Add a `server.buildHeaders` method, where you get the authorization header from
270
- the client request and pass it on to the server request.
271
-
272
- ```javascript
273
- buildHeaders(req, name, type) {
274
- const auth = req.headers.authorization
275
- if (auth) {
276
- return {
277
- Authorization: auth,
41
+ ```graphql
42
+ query films {
43
+ allFilms {
44
+ films {
45
+ id
278
46
  }
279
47
  }
280
-
281
- return {}
282
48
  }
283
49
  ```
284
50
 
285
- ## Full working example
286
-
287
- ```javascript
288
- module.exports = {
289
- modules: ['nuxt-graphql-middleware'],
51
+ Your query is now available via the useGraphqlQuery() composable:
290
52
 
291
- graphqlMiddleware: {
292
- graphqlServer: 'http://example.com/graphql'
293
- endpointNamespace: '/__api'
294
- debug: true
295
- queries: {
296
- route: '~/pages/query.route.graphql',
297
- articles: '~/pages/articles/query.articles.graphql',
298
- footer: '~/components/Footer/query.footer.graphql',
299
- },
300
- mutations: {
301
- createPost: '~/components/Comment/mutation.createPost.graphql'
302
- },
303
- outputPath: '~/graphql_tmp'
304
- plugin: {
305
- enabled: true,
306
- cacheInBrowser: true,
307
- cacheInServer: false,
308
- },
309
- typescript: {
310
- enabled: true,
311
- schemaOutputPath: '~/schema',
312
- typesOutputPath: '~/types',
313
- schemaOptions: {
314
- headers: {
315
- Authorization: 'Basic ' + process.env.BASIC_AUTH
316
- }
317
- }
318
- },
319
- server: {
320
- middleware: function(req, res, next) {
321
- if (isLoggedIn(req.headers.cookie)) {
322
- return next()
323
- }
324
- res.status(403).send()
325
- },
326
- fetchOptions: {
327
- headers: {
328
- Authorization: 'Basic ' + process.env.BASIC_AUTH
329
- }
330
- },
331
- buildHeaders: function (req, name, type) {
332
- if (isLoggedIn(req.headers.cookie)) {
333
- if (type === 'mutation') {
334
- return {
335
- Authorization: 'Basic ' + process.env.BASIC_AUTH_WRITE
336
- }
337
- }
338
- }
339
- },
340
- onQueryResponse: function(response, req, res) {
341
- return res.json({
342
- data: response.data,
343
- time: Date.now()
344
- })
345
- },
346
- onQueryError: function(error, req, res) {
347
- return res.status(500).send()
348
- },
349
- onMutationResponse: function(response, req, res) {
350
- return res.json({
351
- data: response.data,
352
- time: Date.now()
353
- })
354
- }
355
- onMutationError: function(error, req, res) {
356
- return res.status(500).send()
357
- }
358
- }
359
- }
360
- }
53
+ ```typescript
54
+ const { data } = await useGraphqlQuery('films')
55
+ console.log(data.allFilms.films)
361
56
  ```
362
57
 
363
- # TODO
364
- - Pass port to client plugin
58
+ Alternatively you can also call
59
+ `http://localhost:3000/api/graphql_middleware/query/films` to get the same
60
+ result.
package/dist/module.d.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  import { NuxtModule } from '@nuxt/schema';
2
2
  import { H3Event } from 'h3';
3
- import { FetchOptions } from 'ohmyfetch';
3
+ import { FetchOptions, FetchResponse, FetchError } from 'ofetch';
4
4
  import { TypeScriptDocumentsPluginConfig } from '@graphql-codegen/typescript-operations';
5
5
 
6
- declare type GraphqlMiddlewareGraphqlEndpointMethod = (event?: H3Event, operation?: string, operationName?: string) => string;
7
- declare type GraphqlMiddlewareServerFetchOptionsMethod = (event?: H3Event, operation?: string, operationName?: string) => FetchOptions;
6
+ type GraphqlMiddlewareGraphqlEndpointMethod = (event?: H3Event, operation?: string, operationName?: string) => string;
7
+ type GraphqlMiddlewareServerFetchOptionsMethod = (event?: H3Event, operation?: string, operationName?: string) => FetchOptions;
8
+ type GraphqlMiddlewareOnServerResponseMethod = (event: H3Event, response: FetchResponse<any>, operation?: string, operationName?: string) => any;
9
+ type GraphqlMiddlewareOnServerErrorMethod = (event: H3Event, error: FetchError, operation?: string, operationName?: string) => any;
8
10
  interface GraphqlMiddlewareConfig {
9
11
  /**
10
12
  * File glob patterns for the auto import feature.
@@ -47,6 +49,15 @@ interface GraphqlMiddlewareConfig {
47
49
  * ```
48
50
  */
49
51
  documents?: string[];
52
+ /**
53
+ * Wether the useGraphqlQuery, useGraphqlMutation and useGraphqlState
54
+ * composables should be included.
55
+ *
56
+ * @default ```ts
57
+ * true
58
+ * ```
59
+ */
60
+ includeComposables?: boolean;
50
61
  /**
51
62
  * Enable detailled debugging messages.
52
63
  *
@@ -78,7 +89,7 @@ interface GraphqlMiddlewareConfig {
78
89
  */
79
90
  serverApiPrefix?: string;
80
91
  /**
81
- * Provide the options for the ohmyfetch request to the GraphQL server.
92
+ * Provide the options for the ofetch request to the GraphQL server.
82
93
  *
83
94
  * @default undefined
84
95
  *
@@ -97,6 +108,63 @@ interface GraphqlMiddlewareConfig {
97
108
  * ```
98
109
  */
99
110
  serverFetchOptions?: FetchOptions | GraphqlMiddlewareServerFetchOptionsMethod;
111
+ /**
112
+ * Handle the response from the GraphQL server.
113
+ *
114
+ * You can alter the response, add additional properties to the data, get
115
+ * and set headers, etc.
116
+ *
117
+ * ```ts
118
+ * import type { H3Event } from 'h3'
119
+ * import type { FetchResponse } from 'ofetch'
120
+ *
121
+ * function onServerResponse(event: H3Event, graphqlResponse: FetchResponse) {
122
+ * // Set a static header.
123
+ * event.node.res.setHeader('x-nuxt-custom-header', 'A custom header value')
124
+ *
125
+ * // Pass the set-cookie header from the GraphQL response to the client.
126
+ * const setCookie = graphqlResponse.headers.get('set-cookie')
127
+ * if (setCookie) {
128
+ * event.node.res.setHeader('set-cookie', setCookie)
129
+ * }
130
+ *
131
+ * // Add additional properties to the response.
132
+ * graphqlResponse._data.__customProperty = ['My', 'values']
133
+ *
134
+ * // Return the GraphQL response.
135
+ * return graphqlResponse._data
136
+ * }
137
+ * ```
138
+ */
139
+ onServerResponse?: GraphqlMiddlewareOnServerResponseMethod;
140
+ /**
141
+ * Handle a fetch error from the GraphQL request.
142
+ *
143
+ * Note that errors are only thrown for responses that are not status
144
+ * 200-299. See https://github.com/unjs/ofetch#%EF%B8%8F-handling-errors for
145
+ * more information.
146
+ *
147
+ * ```ts
148
+ * import { createError } from 'h3'
149
+ * import type { H3Event } from 'h3'
150
+ * import type { FetchError } from 'ofetch'
151
+ *
152
+ * function onServerError(
153
+ * event: H3Event,
154
+ * error: FetchError,
155
+ * operation: string,
156
+ * operationName: string,
157
+ * ) {
158
+ * // Throw a h3 error.
159
+ * throw createError({
160
+ * statusCode: 500,
161
+ * statusMessage: `Couldn't execute GraphQL ${operation} "${operationName}".`,
162
+ * data: error.message
163
+ * })
164
+ * }
165
+ * ```
166
+ */
167
+ onServerError?: GraphqlMiddlewareOnServerErrorMethod;
100
168
  /**
101
169
  * Download the GraphQL schema and store it in the
102
170
  *
@@ -134,8 +202,8 @@ interface GraphqlMiddlewareConfig {
134
202
  codegenConfig?: TypeScriptDocumentsPluginConfig;
135
203
  }
136
204
 
137
- declare type ModuleOptions = GraphqlMiddlewareConfig;
138
- declare type ModuleHooks = {};
205
+ type ModuleOptions = GraphqlMiddlewareConfig;
206
+ type ModuleHooks = {};
139
207
  declare const _default: NuxtModule<GraphqlMiddlewareConfig>;
140
208
 
141
209
  export { ModuleHooks, ModuleOptions, _default as default };
package/dist/module.json CHANGED
@@ -1,5 +1,8 @@
1
1
  {
2
2
  "name": "nuxt-graphql-middleware",
3
3
  "configKey": "graphqlMiddleware",
4
- "version": "3.0.0-beta.2"
4
+ "version": "3.0.0-beta.3",
5
+ "compatibility": {
6
+ "nuxt": "^3.0.0"
7
+ }
5
8
  }