Skip to content

Commit c6979e6

Browse files
authored
Merge pull request #2480 from graphcommerce-org/feature/static-mesh
GraphQL Mesh will now be in read-only mode by default, so only a single instance is created globally
2 parents 545c655 + fbaee27 commit c6979e6

File tree

14 files changed

+142
-917
lines changed

14 files changed

+142
-917
lines changed

.changeset/strange-turkeys-sort.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@graphcommerce/graphql-mesh': patch
3+
---
4+
5+
GraphQL Mesh will now be in read-only mode by default, so only a single instance is created globally. This means it doesn't get recreated on each page compilation and fast refresh. Creating the instance is an expensive operation and can take multiple seconds and during development (and this can happen multiple times during a single change). Now only a single instance is created during development. To make sure changes are picked up during development set the config value `graphqlMeshEditMode: true` in your graphcommerce.config.js or set the env variable `GC_GRAPHQL_MESH_EDIT_MODE=1`. This is the same as the old behavior and this _will_ make the frontend considerably slower.

docs/framework/config.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,11 @@ The Google Tagmanager ID to be used on the site.
251251

252252
This value is required even if you are configuring different values for each locale.
253253

254+
#### graphqlMeshEditMode: boolean = `false`
255+
256+
The GraphQL Mesh will be loaded once and any modifications to resolvers will be ignored. When developing
257+
new resolvers this should be set to true.
258+
254259
#### hygraphManagementApi: string
255260

256261
Hygraph Management API. **Only used for migrations.**

docs/framework/mesh.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ To make modifications to the Mesh configuration, you can:
2727
### Modify the meshrc.yaml:
2828

2929
You can always modify the base configuration of the Mesh by modifying the
30-
`meshrc.yaml` file.
30+
`meshrc.yaml` file. After making always run `yarn codegen` (this can be in a
31+
separate terminal and nextjs will reload it).
3132

3233
### Write a plugin:
3334

@@ -61,6 +62,33 @@ export const meshConfig: FunctionPlugin<typeof meshConfigBase> = (
6162
},
6263
},
6364
],
65+
additionalResolvers: [
66+
...(baseConfig.additionalResolvers ?? []),
67+
'lib/resolvers/my-feature.ts',
68+
],
6469
})
6570
}
6671
```
72+
73+
### Creating additional schema's
74+
75+
During development it might come in handy to write schema extensions even before
76+
any backend work has been done. `AnyFile.graphqls` in the graphql directory will
77+
automatically be picked up and merged with the rest of the schema.
78+
79+
### Creating additional resolvers
80+
81+
In the plugin add additionalResolvers and point to your ts file where the
82+
resolver is.
83+
84+
```tsx
85+
// This MUST be a type import, else there will be a circular dependency.
86+
import type { Resolvers } from '@graphcommerce/graphql-mesh'
87+
88+
const resolvers: Resolvers = {}
89+
```
90+
91+
To make sure changes are picked up during development set the config value
92+
`graphqlMeshEditMode: true` in your graphcommerce.config.js or set the env
93+
variable `GC_GRAPHQL_MESH_EDIT_MODE=1`. This _will_ make the frontend
94+
considerably slower.

packages/cli/dist/bin/mesh.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import dotenv from 'dotenv';
99
import 'tsx/cjs';
1010
import 'tsx/esm';
1111
import yaml from 'yaml';
12-
import { cosmiconfig, defaultLoaders } from 'cosmiconfig';
1312
import path from 'path';
13+
import { cosmiconfig, defaultLoaders } from 'cosmiconfig';
1414

1515
function customLoader(ext, importFn = defaultImportFn, initialLoggerPrefix = "\u{1F578}\uFE0F Mesh") {
1616
const logger = new DefaultLogger(initialLoggerPrefix).child("config");
@@ -40,7 +40,7 @@ function customLoader(ext, importFn = defaultImportFn, initialLoggerPrefix = "\u
4040
return loader;
4141
}
4242
async function findConfig(options) {
43-
const { configName = "mesh", dir: configDir = "", initialLoggerPrefix } = options || {};
43+
const { configName = "mesh", dir: configDir = "", initialLoggerPrefix } = options;
4444
const dir = path.isAbsolute(configDir) ? configDir : path.join(process.cwd(), configDir);
4545
const explorer = cosmiconfig(configName, {
4646
searchPlaces: [
@@ -169,7 +169,14 @@ const main = async () => {
169169
await promises.writeFile(tmpMeshLocation, yamlString);
170170
await promises.writeFile(
171171
`${meshDir}/.mesh.ts`,
172-
`export * from '${relativePath.split(path$1.sep).join("/")}.mesh'`,
172+
`export type * from '${relativePath.split(path$1.sep).join("/")}.mesh'
173+
export {
174+
getBuiltMesh as getBuiltMeshBase,
175+
execute,
176+
subscribe,
177+
createBuiltMeshHTTPHandler as createBuiltMeshHTTPHandlerBase,
178+
rawServeConfig,
179+
} from '${relativePath.split(path$1.sep).join("/")}.mesh'`,
173180
{ encoding: "utf8" }
174181
);
175182
await graphqlMesh({ ...cliParams, configName: tmpMesh });

packages/cli/src/bin/mesh.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,14 @@ const main = async () => {
165165
// Reexport the mesh to is can be used by packages
166166
await fs.writeFile(
167167
`${meshDir}/.mesh.ts`,
168-
`export * from '${relativePath.split(path.sep).join('/')}.mesh'`,
168+
`export type * from '${relativePath.split(path.sep).join('/')}.mesh'
169+
export {
170+
getBuiltMesh as getBuiltMeshBase,
171+
execute,
172+
subscribe,
173+
createBuiltMeshHTTPHandler as createBuiltMeshHTTPHandlerBase,
174+
rawServeConfig,
175+
} from '${relativePath.split(path.sep).join('/')}.mesh'`,
169176
{ encoding: 'utf8' },
170177
)
171178

packages/graphql-mesh/Config.graphqls

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
extend input GraphCommerceConfig {
2+
"""
3+
The GraphQL Mesh will be loaded once and any modifications to resolvers will be ignored. When developing
4+
new resolvers this should be set to true.
5+
"""
6+
graphqlMeshEditMode: Boolean = false
7+
}
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import type { NextApiRequest, NextApiResponse } from 'next'
2-
import { createBuiltMeshHTTPHandler } from '../.mesh'
3-
4-
const handler = createBuiltMeshHTTPHandler()
2+
import { createBuiltMeshHTTPHandler } from './globalThisMesh'
53

64
// eslint-disable-next-line @typescript-eslint/require-await
75
export const createServer = async (endpoint: string) => {
86
if (endpoint !== '/api/graphql')
97
throw Error('Moving the GraphQL Endpoint is not supported at the moment')
10-
return (req: NextApiRequest, res: NextApiResponse) => {
8+
9+
const handler = createBuiltMeshHTTPHandler()
10+
return async (req: NextApiRequest, res: NextApiResponse) => {
1111
res.setHeader('Access-Control-Allow-Origin', req.headers.origin || '*')
1212
const requestedHeaders = req.headers['access-control-request-headers']
1313
if (requestedHeaders) {
@@ -20,6 +20,6 @@ export const createServer = async (endpoint: string) => {
2020
return
2121
}
2222

23-
handler(req, res)
23+
await handler(req, res)
2424
}
2525
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import type { MeshInstance } from '@graphql-mesh/runtime'
2+
import type {
3+
ServerAdapter,
4+
ServerAdapterBaseObject,
5+
ServerAdapterRequestHandler,
6+
} from '@whatwg-node/server'
7+
import { createBuiltMeshHTTPHandlerBase, getBuiltMeshBase } from '../.mesh'
8+
9+
type MeshHTTPHandler<TServerContext = Record<string, unknown>> = ServerAdapter<
10+
TServerContext,
11+
ServerAdapterBaseObject<TServerContext, ServerAdapterRequestHandler<TServerContext>>
12+
>
13+
14+
declare global {
15+
// eslint-disable-next-line vars-on-top, no-var
16+
var buildMesh: Promise<MeshInstance> | undefined
17+
18+
// eslint-disable-next-line vars-on-top, no-var
19+
var builtMeshHandler: MeshHTTPHandler | undefined
20+
}
21+
22+
const shouldGlobalThisMeshBeCreated =
23+
process.env.NODE_ENV === 'development' && import.meta.graphCommerce.graphqlMeshEditMode !== true
24+
25+
/**
26+
* We are creating a global instance of the mesh so it doesn't get recreated on every change.
27+
* Creating the instance is a very long operation and with sufficiently complex schema's it can take
28+
* multiple seconds. During development it can happen multiple times during a single change.
29+
*
30+
* During development this creates a big advantage as we do not recreate the mesh on every reload.
31+
* This makes development a lot faster.
32+
*
33+
* The disadvantage of this is that the mesh and any resolvers custom resolver will not be refreshed
34+
* whenever code changes are made, to enable this set the config value `graphqlMeshEditMode: true`
35+
* in your graphcommerce.config.js or set the env variable `GC_GRAPHQL_MESH_EDIT_MODE=1`.
36+
*/
37+
export function getBuiltMesh() {
38+
if (shouldGlobalThisMeshBeCreated) {
39+
globalThis.buildMesh ??= getBuiltMeshBase()
40+
return globalThis.buildMesh
41+
}
42+
return getBuiltMeshBase()
43+
}
44+
45+
/**
46+
* Same as globalThisGetBuiltMesh but for the mesh handler. As the handler uses additional logic so
47+
* we can't re-use globalThisGetBuiltMesh.
48+
*/
49+
export function createBuiltMeshHTTPHandler(): MeshHTTPHandler {
50+
if (shouldGlobalThisMeshBeCreated) {
51+
globalThis.builtMeshHandler ??= createBuiltMeshHTTPHandlerBase() as MeshHTTPHandler
52+
return globalThis.builtMeshHandler
53+
}
54+
return createBuiltMeshHTTPHandlerBase() as MeshHTTPHandler
55+
}

packages/graphql-mesh/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
/* eslint-disable import/export */
12
export * from './api/createEnvelop'
23
export * from './api/apolloLink'
34
export * from './.mesh'
5+
// @ts-expect-error getBuiltMesh and createBuiltMeshHTTPHandler are re-exported here and override the export from .mesh
6+
export * from './api/globalThisMesh'
47
export * from './utils/traverseSelectionSet'

packages/graphql-mesh/tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"exclude": ["**/node_modules", "**/.*/"],
33
"include": ["**/*.ts", "**/*.tsx"],
4-
"extends": "@graphcommerce/typescript-config-pwa/node.json"
4+
"extends": "@graphcommerce/typescript-config-pwa/nextjs.json"
55
}

0 commit comments

Comments
 (0)