Skip to content

Commit

Permalink
feat(apollo): migrate package on Apollo 4
Browse files Browse the repository at this point in the history
BREAKING CHANGE: all implementation and configuration of @tsed/apollo have changed.
  • Loading branch information
Romakita committed Sep 14, 2024
1 parent 596b261 commit 4e84844
Show file tree
Hide file tree
Showing 32 changed files with 744 additions and 767 deletions.
138 changes: 134 additions & 4 deletions docs/tutorials/graphql-apollo.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ Ts.ED provides a module to create multiple Apollo server and bind it with Ts.ED

## Feature

::: warning
Since v7.70.0, Ts.ED use Apollo Server v4. If you are using Apollo Server v3, you can use the `@tsed/apollo@7.69.0` package.
:::

- Create [Apollo](https://www.apollographql.com/docs/apollo-server/api/apollo-server.html) Server and bind it with
Ts.ED,
- Create multiple servers,
Expand All @@ -27,15 +31,15 @@ Ts.ED provides a module to create multiple Apollo server and bind it with Ts.ED
<Tab label="Express.js">

```bash
npm install --save @tsed/apollo graphql apollo-server-express
npm install --save @tsed/apollo graphql @apollo/server
npm install --save-dev apollo-server-testing
```

</Tab>
<Tab label="Koa.js">

```bash
npm install --save @tsed/apollo graphql apollo-server-koa
npm install --save @tsed/apollo graphql @apollo/server @as-integration/koa
npm install --save-dev apollo-server-testing
```

Expand Down Expand Up @@ -160,15 +164,15 @@ ApolloService (or TypeGraphQLService) lets you retrieve an instance of ApolloSer
import {AfterRoutesInit} from "@tsed/common";
import {Inject, Injectable} from "@tsed/di";
import {ApolloService} from "@tsed/apollo";
import {ApolloServerBase} from "apollo-server-core";
import {ApolloServer} from "@apollo/server";

@Injectable()
export class UsersService implements AfterRoutesInit {
@Inject()
private ApolloService: ApolloService;
// or private typeGraphQLService: TypeGraphQLService;

private server: ApolloServerBase;
private server: ApolloServer;

$afterRoutesInit() {
this.server = this.apolloService.get("server1")!;
Expand All @@ -179,6 +183,132 @@ export class UsersService implements AfterRoutesInit {
For more information about ApolloServer, look at its
documentation [here](https://www.apollographql.com/docs/apollo-server/api/apollo-server.html);

## DataSources

Apollo Server provides a mechanism to fetch data from a REST API or a database. This mechanism is called DataSources.
Ts.ED allows you to register your DataSources using the `@DataSource` decorator.

```typescript
import {DataSource} from "@tsed/apollo";
import {RESTDataSource} from "@apollo/datasource-rest";

@DataSource()
export class MyDataSource extends RESTDataSource {
@InjectContext()
protected $ctx: PlatformContext;

@Constant("envs.MY_API_URL", "http://localhost:8001")
private baseURL: string;

getMyData(id: string) {
return this.get(`/rest/calendars/${id}`, {
headers: {
Authorization: `Bearer ${this.$ctx.request.get("authorization")}`
}
});
}
}
```

## Alter Apollo Context <Badge text="7.70.0+"/>

You can alter the Apollo context by using the `$alterApolloContext` hook. This hook is called each the GraphQL request is handled.

For example, you can call a service to get the user scope from the request token.

```typescript
import type {AlterApolloContext, ApolloContext} from "@tsed/apollo";
import {PlatformContext} from "@tsed/common";
import {Injectable} from "@tsed/di";
import {AuthService} from "../auth/AuthService";

export interface CustomApolloContext extends ApolloContext {
scope: string;
token: string | undefined;
}

@Injectable()
export class MyModule implements AlterApolloContext {
@Inject()
protected authService: AuthService;

async $alterApolloContext(context: ApolloContext, $ctx: PlatformContext): CustomApolloContext {
const token = $ctx.request.get("authorization");

return {
...context,
token,
scope: await this.authService.getScope(token)
};
}
}
```

Now, your context will be updated with the `authScope` property, and you can access it in your DataSources or resolvers.

```typescript
import {DataSource, InjectApolloContext, ApolloContext, InjectApolloContext} from "@tsed/apollo";
import {Constant, Opts} from "@tsed/di";
import {RESTDataSource} from "@apollo/datasource-rest";

@DataSource()
export class MyDataSource extends RESTDataSource {
@InjectContext()
protected $ctx: PlatformContext;

@Constant("envs.MY_BACK_URL", "http://localhost:8001")
protected baseURL: string;

@InjectApolloContext()
protected context: CustomApolloContext;

constructor(server: ApolloServer, logger: Logger) {
super({
logger,
cache: server.cache
});
}

willSendRequest(path, request) {
request.headers["authorization"] = this.context.token;
}

getMyData(id: string) {
return this.get(`/rest/calendars/${id}`);
}
}
```

Here another example using `apollo-datasource-http`:

```ts
import {DataSource, InjectApolloContext, ApolloContext, InjectApolloContext} from "@tsed/apollo";
import {ApolloServer} from "@apollo/server";
import {Opts, Configuration} from "@tsed/di";

@DataSource()
class MoviesAPI extends HTTPDataSource {
constructor(@Opts {token}: CustomApolloContext, server: ApolloServer, logger: Logger, @Configuration() configuration: Configuration) {
// the necessary arguments for HTTPDataSource
super(configuration.get("envs.MOVIES_API_URL"), {
logger
});

// We need to call the initialize method in our data source's
// constructor, passing in our cache and contextValue.
this.initialize({cache: server.cache, context: token});
}

getMovie(id: string) {
return this.get(`movies/${encodeURIComponent(id)}`);
}
}
```

::: warning
Injecting ApolloServer on constructor is only possible inside a DataSource class. If you need to inject ApolloServer in another class, you can use the `ApolloService` to retrieve the server instance.
:::

## Author

<GithubContributors users="['Romakita']"/>
Expand Down
8 changes: 3 additions & 5 deletions docs/tutorials/graphql-nexus.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,15 @@ This example need to be used with `@tsed/apollo` module. So, you must install it
<Tab label="Express.js">

```bash
npm install --save @tsed/apollo
npm install --save nexus graphql apollo-server-express
npm install --save @tsed/apollo nexus graphql @apollo/server
npm install --save-dev apollo-server-testing
```

</Tab>
<Tab label="Koa.js">

```bash
npm install --save @tsed/apollo graphql
npm install --save nexus graphql apollo-server-koa
npm install --save @tsed/apollo nexus graphql @apollo/server @as-integration/koa
npm install --save-dev apollo-server-testing
```

Expand Down Expand Up @@ -90,7 +88,7 @@ a @@DataSourceService@@ decorator to declare a DataSource which will be injected

```typescript
import {DataSource} from "@tsed/typegraphql";
import {RESTDataSource} from "apollo-datasource-rest";
import {RESTDataSource} from "@apollo/datasource-rest";
import {User} from "../models/User";
@DataSource()
export class UserDataSource extends RESTDataSource {
Expand Down
46 changes: 29 additions & 17 deletions docs/tutorials/graphql-typegraphql.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,15 @@ To begin, install the `@tsed/typegraphql` package:
<Tab label="Express.js">

```bash
npm install --save @tsed/typegraphql graphql apollo-server-express
npm install --save type-graphql apollo-datasource apollo-datasource-rest
npm install --save @tsed/apollo graphql type-graphql @apollo/server @apollo/datasource-rest
npm install --save-dev apollo-server-testing
```

</Tab>
<Tab label="Koa.js">

```bash
npm install --save @tsed/typegraphql graphql apollo-server-koa
npm install --save type-graphql apollo-datasource apollo-datasource-rest
npm install --save @tsed/apollo graphql type-graphql @apollo/server @as-integration/koa @apollo/datasource-rest
npm install --save-dev apollo-server-testing
```

Expand All @@ -49,22 +47,18 @@ import "@tsed/typegraphql";
import "./resolvers/index"; // barrel file with all resolvers

@Configuration({
typegraphql: {
apollo: {
server1: {
// GraphQL server configuration
// See options descriptions on https://www.apollographql.com/docs/apollo-server/api/apollo-server.html
path: "/",
playground: true, // enable playground GraphQL IDE. Set false to use Apollo Studio
playground: true // enable playground GraphQL IDE. Set false to use Apollo Studio

// resolvers?: (Function | string)[];
// dataSources?: Function;
// server?: (config: Config) => ApolloServer;

// Apollo Server options
// See options descriptions on https://www.apollographql.com/docs/apollo-server/api/apollo-server.html
serverConfig: {
plugins: []
}

// plugins: []
// middlewareOptions?: ServerRegistration;

// type-graphql
Expand Down Expand Up @@ -210,17 +204,35 @@ a @@DataSourceService@@ decorator to declare a DataSource which will be injected

```typescript
import {DataSource} from "@tsed/typegraphql";
import {RESTDataSource} from "apollo-datasource-rest";
import {RESTDataSource} from "@apollo/datasource-rest";
import {User} from "../models/User";
import {DataSource, InjectApolloContext, ApolloContext, InjectApolloContext} from "@tsed/apollo";
import {Constant, Opts} from "@tsed/di";
import {RESTDataSource} from "@apollo/datasource-rest";

@DataSource()
export class UserDataSource extends RESTDataSource {
constructor() {
super();
this.baseURL = "https://myapi.com/api/users";
@InjectContext()
protected $ctx: PlatformContext;

@Constant("envs.USERS_URL", "https://myapi.com/api/users")
protected baseURL: string;

@InjectApolloContext()
protected context: CustomApolloContext;

constructor(server: ApolloServer, logger: Logger) {
super({
logger,
cache: server.cache
});
}

willSendRequest(path, request) {
request.headers["authorization"] = this.context.token;
}

getUserById(id: string): Promise<User> {
getUserById(id: string) {
return this.get(`/${id}`);
}
}
Expand Down
Loading

0 comments on commit 4e84844

Please sign in to comment.