Skip to content

Defer #3819

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 29 commits into
base: main
Choose a base branch
from
Draft

Defer #3819

Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
8b2291d
Add imports for experimental execution and some initial types
patrick91 Mar 13, 2025
0cd1d31
Progress with fake data
patrick91 Mar 13, 2025
81f2590
Fix directive
patrick91 Mar 17, 2025
6d9a9e0
Experimental execution disabled by default
patrick91 Mar 17, 2025
ebd3a54
Revert flag
patrick91 Mar 17, 2025
e00bc01
Rename field so we don't get "did you mean" errors
patrick91 Mar 17, 2025
cc959b9
Update schema
patrick91 Mar 17, 2025
c074f72
Mostly working already
patrick91 Mar 18, 2025
454a9a8
Initial code for e2e tests
patrick91 Mar 19, 2025
a0c0076
Structure
patrick91 Mar 19, 2025
b6b75f6
WIP
patrick91 Mar 19, 2025
da1bcb5
Allow failed
patrick91 Mar 24, 2025
3137b25
Push missing stuff
patrick91 Mar 25, 2025
f846524
Update graphiql
patrick91 Mar 25, 2025
118e785
It works if you return a blog post :D
patrick91 Mar 25, 2025
10a10ca
Workflow
patrick91 Mar 25, 2025
30f06b5
Merge branch 'main' into feature/defer
patrick91 Mar 25, 2025
a3780b6
Remove unused files
patrick91 Mar 25, 2025
addf566
Non hard-coded labels
patrick91 Mar 31, 2025
057cd4a
Add release notes
patrick91 Mar 31, 2025
31bd773
Fix type
patrick91 Mar 31, 2025
a64b402
Fix type
patrick91 Mar 31, 2025
01a2c63
Implement Streamable
patrick91 Apr 1, 2025
36bc1a9
Fix annotation
patrick91 Apr 2, 2025
be9949c
⚡️ Speed up function `process_result` by 100% in PR #3819 (`feature/d…
codeflash-ai[bot] Apr 15, 2025
8102999
Merge branch 'main' into feature/defer
patrick91 Apr 16, 2025
354414f
Raise if not on graphql core 3.3.0
patrick91 Apr 16, 2025
ef62c30
Fix tests
patrick91 Apr 16, 2025
876d580
Skip defer test
patrick91 Apr 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 17 additions & 16 deletions .alexrc
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
{
"allow": [
"black",
"hook",
"hooks",
"failure",
"period",
"execute",
"executed",
"executes",
"execution",
"reject",
"special",
"primitive",
"invalid",
"failed"
]
"allow": [
"black",
"hook",
"hooks",
"failure",
"period",
"execute",
"executed",
"executes",
"execution",
"reject",
"special",
"primitive",
"invalid",
"failed",
"crash"
]
}
43 changes: 43 additions & 0 deletions .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: 🎭 E2E Tests

on:
push:
branches: [main]
pull_request:
branches: [main]
paths:
- "e2e/**"
- ".github/workflows/e2e-tests.yml"

jobs:
e2e-tests:
name: 🎭 Run E2E Tests
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Setup Bun
uses: oven-sh/setup-bun@v2

- name: Install dependencies
run: |
cd e2e
bun install

- name: Install Playwright browsers
run: |
cd e2e
bunx playwright install --with-deps

- name: Run Playwright tests
run: |
cd e2e
bun test

- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: e2e/playwright-report/
retention-days: 30
1 change: 1 addition & 0 deletions e2e/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.graphql.ts auto eol=lf
29 changes: 29 additions & 0 deletions e2e/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

!src/lib/

playwright-report/
test-results/
54 changes: 54 additions & 0 deletions e2e/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# React + TypeScript + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

Currently, two official plugins are available:

- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh

## Expanding the ESLint configuration

If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:

```js
export default tseslint.config({
extends: [
// Remove ...tseslint.configs.recommended and replace with this
...tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
...tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
...tseslint.configs.stylisticTypeChecked,
],
languageOptions: {
// other options...
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
},
})
```

You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:

```js
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'

export default tseslint.config({
plugins: {
// Add the react-x and react-dom plugins
'react-x': reactX,
'react-dom': reactDom,
},
rules: {
// other rules...
// Enable its recommended typescript rules
...reactX.configs['recommended-typescript'].rules,
...reactDom.configs.recommended.rules,
},
})
```
45 changes: 45 additions & 0 deletions e2e/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import asyncio # noqa: INP001

import strawberry
from strawberry.schema.config import StrawberryConfig


@strawberry.type
class Comment:
id: strawberry.ID
content: str


@strawberry.type
class BlogPost:
id: strawberry.ID
title: str
content: str

@strawberry.field
async def comments(self) -> list[Comment]:
await asyncio.sleep(2)
return [
Comment(id=strawberry.ID("Comment:1"), content="Great post!"),
Comment(id=strawberry.ID("Comment:2"), content="Thanks for sharing!"),
]


@strawberry.type
class Query:
@strawberry.field
async def hello(self, delay: float = 0) -> str:
await asyncio.sleep(delay)
return "Hello, world!"

@strawberry.field
async def blog_post(self, id: strawberry.ID) -> BlogPost:
return BlogPost(id=id, title="My Blog Post", content="This is my blog post.")


schema = strawberry.Schema(
query=Query,
config=StrawberryConfig(
enable_experimental_incremental_execution=True,
),
)
134 changes: 134 additions & 0 deletions e2e/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import {
buildSchema,
experimentalExecuteIncrementally,
GraphQLObjectType,
GraphQLString,
GraphQLID,
GraphQLNonNull,
GraphQLList,
GraphQLFloat,
GraphQLSchema,
GraphQLDirective,
GraphQLBoolean,
GraphQLInt,
DirectiveLocation,
} from "graphql";
import { createHandler } from "graphql-http/lib/use/express";
import express from "express";
import cors from "cors";

// Simulate async delay
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

// Define directives
const DeferDirective = new GraphQLDirective({
name: "defer",
locations: [
DirectiveLocation.FRAGMENT_SPREAD,
DirectiveLocation.INLINE_FRAGMENT,
],
args: {
if: { type: GraphQLBoolean },
label: { type: GraphQLString },
},
});

const StreamDirective = new GraphQLDirective({
name: "stream",
locations: [DirectiveLocation.FIELD],
args: {
if: { type: GraphQLBoolean },
label: { type: GraphQLString },
initialCount: { type: GraphQLInt, defaultValue: 0 },
},
});

// Define types using GraphQLObjectType
const CommentType = new GraphQLObjectType({
name: "Comment",
fields: {
id: { type: new GraphQLNonNull(GraphQLID) },
content: { type: new GraphQLNonNull(GraphQLString) },
},
});

const BlogPostType = new GraphQLObjectType({
name: "BlogPost",
fields: {
id: { type: new GraphQLNonNull(GraphQLID) },
title: { type: new GraphQLNonNull(GraphQLString) },
content: { type: new GraphQLNonNull(GraphQLString) },
comments: {
type: new GraphQLNonNull(
new GraphQLList(new GraphQLNonNull(CommentType)),
),
resolve: async () => {
await delay(4000);
return [
{ id: "1", content: "Great post!" },
{ id: "2", content: "Thanks for sharing!" },
];
},
},
},
});

const QueryType = new GraphQLObjectType({
name: "Query",
fields: {
hello: {
type: new GraphQLNonNull(GraphQLString),
args: {
delay: { type: GraphQLFloat, defaultValue: 0 },
},
resolve: async (_: unknown, { delay: delayMs }: { delay: number }) => {
await delay(delayMs * 1000);
return "Hello, world!";
},
},
blogPost: {
type: new GraphQLNonNull(BlogPostType),
args: {
id: { type: GraphQLID },
},
resolve: (_: unknown, { id }: { id: string }) => {
return {
id,
title: "My Blog Post",
content: "This is my blog post.",
};
},
},
},
});

// Create the schema
const schema = new GraphQLSchema({
query: QueryType,
directives: [DeferDirective, StreamDirective],
});

const app = express();

// Enable CORS for all routes
app.use(
cors({
origin: "http://localhost:5173", // Allow requests from your frontend
methods: ["GET", "POST", "OPTIONS"], // Allow these HTTP methods
allowedHeaders: ["Content-Type", "Authorization"], // Allow these headers
credentials: true, // Allow credentials (cookies, authorization headers, etc.)
}),
);

// Create and use the GraphQL handler.
app.all(
"/graphql",
createHandler({
schema,
execute: experimentalExecuteIncrementally,
}),
);

app.listen(4000, () => {
console.log("Server is running on port 4000");
});
Loading
Loading