Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/orta/redwood-codegen-api-types
Replacement types generator for your Redwood API
https://github.com/orta/redwood-codegen-api-types
deno graphql redwood typescript
Last synced: 16 days ago
JSON representation
Replacement types generator for your Redwood API
- Host: GitHub
- URL: https://github.com/orta/redwood-codegen-api-types
- Owner: orta
- License: mit
- Archived: true
- Created: 2022-11-28T06:29:03.000Z (about 2 years ago)
- Default Branch: main
- Last Pushed: 2023-11-30T10:38:31.000Z (about 1 year ago)
- Last Synced: 2024-09-26T11:01:36.407Z (5 months ago)
- Topics: deno, graphql, redwood, typescript
- Language: TypeScript
- Homepage: https://github.com/sdl-codegen/sdl-codegen
- Size: 259 KB
- Stars: 11
- Watchers: 3
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
Now: https://github.com/sdl-codegen/sdl-codegen
The long term goals of this repo is to get it in redwood, over time I will start moving the tech stack for this repo to be more inline with the code used inside redwood core.
## How to use this in a Redwood project
In your root, add the dependency:
```sh
yarn -W add -D @orta/redwood-codegen-api-types
```Then to run the codegen locally:
```sh
yarn redwood-alt-api-codegen
```There are two params you can optionally pass:
- [1] The cwd of your Redwood project: (`.` default)
- [2] The folder you want the types to get added to: (`./api/src/lib/types` default)With the following extra possible args:
- [`--eslint-fix`] - auto apply ESLint fixes to the new changed files
- [`--rm-default-api-types] - remove the default API type file at `[app_root]/api/types/graphql.d.ts`## What does that do?
The codegen will generate a `.d.ts` file for each service in your Redwood project, and 2 shared `.d.ts` files which contains all of the types for the whole schema. The types are generated from the GraphQL SDL, and the Prisma schema combined, so each `[service].d.ts` will only contain code which is relevant to that file.
It is now pretty much feature complete, having taken the number of compiler errors in my app down from ~160 to 0.
## Why
Redwood's type generation is pretty reasonable for most projects, but I've found after ~15 months of using Redwood when I turned on TypeScript's strict mode, I've never once achieved 0 compiler messages and performance for auto-complete/errors was not great inside the API files.
This... is a mixed bag, I'm reasonably sure that the runtime code is right but I was struggling to understand the compiler errors due to the extreme flexibility in the types generated by [graphql-codegen](https://the-guild.dev/graphql/codegen) and when I tried to solve some of the issues in userland it kept feeling like I was applying too much type-foo which would be harder for others to understand and maintain.
So, this project is what I have been referring to as 'relay style' types codegen, where each service in the Redwood project gets its own `.d.ts` file which is hyper specific - taking into account the resolvers defined, the GraphQL schema and the types from Prisma.
I like to think of it as taking all of the work which happens in the type system, from types like this:
```ts
export type AccountRelationResolvers<
ContextType = RedwoodGraphQLContext,
ParentType extends ResolversParentTypes["Account"] = ResolversParentTypes["Account"]
> = {
createdAt?: RequiredResolverFn<
ResolversTypes["DateTime"],
ParentType,
ContextType
>;
email?: RequiredResolverFn;
users?: RequiredResolverFn<
Array,
ParentType,
ContextType
>;
};export type ResolversParentTypes = {
Account: MergePrismaWithSdlTypes<
PrismaAccount,
MakeRelationsOptional,
AllMappedModels
>;
};// ...
```and instead manually do the work at runtime, with an understanding of how Redwood works to simply be 'fully resolved' smallest possible types:
```ts
import type { Account as PAccount, User as PUser } from "@prisma/client";type AccountAsParent = PAccount & { users: () => Promise };
export interface AccountTypeResolvers {
/** SDL: users: [User!]! */
users: (
args: undefined,
obj: {
root: AccountAsParent;
context: RedwoodGraphQLContext;
info: GraphQLResolveInfo;
}
) => PUser[] | Promise | (() => Promise);
}
```Obviously this is considerably less flexible than before, but the goal is to be exactly what I need for my large Redwood project and then if folks are interested in the same problems, we can collab on making it more flexible.
I'm not really sure it makes sense for the Redwood team to think about upstreaming the changes, mainly because there's a bunch of codebases out there with all sorts of edge cases - and I don't have time to deal with other people's edge cases.
---
### Clever features
- Separate sets of SDL types for whether the type is used in an input or output position (e.g. SDL `User` in vs Prisma use `User` out)
- Merges comments from Prisma file + GQL into .d.ts files
- You can set a generic param on a type resolver which can extend the parent's type:```ts
export const User: UserTypeResolvers<{ cachedAccount?: Account }> = {
account: (args, { root }) => {
if (root.cachedAccount) return root.cachedAccount;
return db.account.findUnique({ where: { id: root.accountID } });
},
};
```- In dev mode it can run eslint fixes over the files it generates, so it is formatted correctly
- You can use [an eslint rule to auto-hook](https://community.redwoodjs.com/t/custom-eslint-rules-in-redwood/4379) up the imports for you
- Resolvers show what their SDL source of truth is when you hover over them
- It manually type narrow's for your `async` resolvers, so that you can trivially re-use them from outside the service file e.g. on tests. Params are also made optional on the types if they are not included in the resolver's fn.---
You can see what it looks like when running on a small, but real, Redwood project here:
- [app](tests/vendor/soccersage.io-main)
- [generated types](tests/vendor/soccersage-output)## How to work in this repo
- Install deno
- Clone the repo
- Run `deno task dev` to start the dev server
- Run tests via `deno task test`The dev server will re-run against the fixtures in `tests/vendor`, you can use git to work with the diff.
You can make a `.env` in the root, and the dev server will _also_ run against these paths:
```s
MAIN_APP_PATH="/home/orta/dev/app/"
MAIN_TYPES_DEPLOY="/home/orta/dev/redwood-codegen-api-types/ignored/"
```This is useful if you want to test your code against a real Redwood project, while also being able to see how it changes with the vendored sdl + services.
## Done
- Generating a shared library of types for the whole schema (for referencing inside your resolvers)
- Query / Mutation resolvers are correctly typed
- Comments from Prisma file, and SDL are included in the generated types
- Resolvers on specific models need to be added
- Create an internal representation of a GQL type which adds an optional marker to resolvers defined in the file.
- Unit test runner
- Deno to npm module for ease of deploy## TODO
- Watch mode
- Create an 'unused resolvers' interface for auto-complete on the main type resolvers? Unsure, feels a bit messy but might make for a good DX for redwood newbies## Deployment
This is a Deno app which used 'DNT' to deploy to npm, so you can run `deno task build:npm [version]` to build a node compatible version of the app.
```sh
deno task build:npm 0.0.1
deno task deploy:npm
```