Skip to content
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

feat: added typescript support and updated docs accordingly #94

Merged
merged 10 commits into from
Jan 8, 2021
3 changes: 3 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ coverage

# Plugin
lib/plugin.js

# Typings
/**/*.d.ts
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
- RESTful methods
- Adaptive SDK for API entities
- Handle errors with hooks
- TypeScript support

[📖  Read the documentation](https://strapi.nuxtjs.org)

Expand Down
1 change: 1 addition & 0 deletions docs/content/en/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ features:
- RESTful methods
- Adaptive SDK for API entities
- Handle errors with hooks
- TypeScript support
---

<img src="/preview.png" class="light-img" width="1280" height="640" alt=""/>
Expand Down
2 changes: 1 addition & 1 deletion docs/content/en/proxy.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Using a proxy
description: 'Use Strapi behind a proxy with Nuxt Proxy module'
position: 7
position: 8
category: Advanced
fullscreen: true
---
Expand Down
155 changes: 155 additions & 0 deletions docs/content/en/typescript.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
---
title: Usage with Typescript
description: 'Discover how you can setup your project to integrate Strapi with TypeScript'
position: 7
category: Advanced
---

## Setup

Thanks to [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html),
you can tell the TypeScript compiler where to find the `$strapi` types by adding these lines to your
`tsconfig.json`.

```json[tsconfig.json]
{
"compilerOptions": {
"types": [
"@nuxtjs/strapi"
]
}
}
```

## Usage

You now have access to `this.$strapi` inside your components and to `ctx.$strapi` inside
`asyncData`, `fetch`, `plugins`, `middlewares` and `nuxtServerInit`.

### In component methods
```vue
LuckeeDev marked this conversation as resolved.
Show resolved Hide resolved
<script lang="ts">
import Vue from 'vue'

interface BlogPost {
title: string;
description: string;
}

export default Vue.extend({
async data () {
const post = await this.$strapi.create<BlogPost>(
'posts',
{
title: 'Welcome to Strapi',
description: 'Strapi is awesome!'
}
)

return {
post
}
}
})
</script>
```

> Notice how you can define the type of the query parameters and of the returned value using generics.

### Inside methods that use `context`
```vue
<script lang="ts">
import Vue from 'vue'
import { Context } from '@nuxt/types'

interface BlogPost {
title: string;
description: string;
}

export default Vue.extend({
async asyncData (ctx: Context) {
const posts = await ctx.$strapi.find<BlogPost[]>(
'posts'
)

return {
posts
}
}
})
</script>
```

## Known issues

There are some known issues with the current implementation of TypeScript in the library,
so we have listed some workarounds.

### Entity shortcuts

The current TypeScript implementation doesn't support entity shortcuts: your code may work,
but it will throw type checking errors. This is why we made the `entity` parameter needed in
every method which involves a query.

You can however define the entities available to your Vue methods with
[string literal types](https://www.typescriptlang.org/docs/handbook/literal-types.html#string-literal-types)
as described below.

```vue
<script lang="ts">
import Vue from 'vue'
import { Context } from '@nuxt/types'

interface BlogPost {
title: string;
description: string;
}

type Entities = 'posts' | 'projects'

export default Vue.extend({
async asyncData (ctx: Context) {
const posts = await ctx.$strapi.find<BlogPost[], Entities>(
'posts'
)

return {
posts
}
}
})
</script>
```

### Query params

Some query methods won't be accepted by the compiler if you define generics as in `$strapi.find<BlogPost[]>`
(e.g.: the array methods described [here](https://strapi.nuxtjs.org/strapi#findentity-params)).

You can get around this issue by doing something like below.

```vue
<script lang="ts">
import Vue from 'vue'

interface BlogPost {
title: string;
description: string;
}

export default Vue.extend({
async asyncData (ctx: Context) {
const posts: BlogPost[] = await this.$strapi.find(
'posts'
)

return {
posts
}
}
})
</script>
```

> You can always declare the return type by assigning a type to the variable instead of the method.
170 changes: 170 additions & 0 deletions lib/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import Vue from 'vue';
import { Context, NuxtAppOptions } from '@nuxt/types';
LuckeeDev marked this conversation as resolved.
Show resolved Hide resolved
import { NuxtHTTPInstance } from '@nuxt/http';
import { NuxtCookies } from 'cookie-universal-nuxt';

type NuxtStrapiQueryParams<T> = T | Record<string, any>;

interface NuxtStrapiGraphQLParams {
query: string;
}

interface NuxtStrapiRegistrationData {
username: string;
email: string;
password: string;
}

interface NuxtStrapiLoginData {
/**
* Can be either the email or the username set by the user.
* */
identifier: string;
password: string;
}

interface NuxtStrapiEmailData {
email: string;
}

interface NuxtStrapiResetPasswordData {
code: string;
password: string;
passwordConfirmation: string;
}

export interface NuxtStrapi {
/**
* Use this object to access details about the
* authenticated user or to directly set a user prop.
* */
user: Record<string, any>;

/**
* Get entries.
* Returns entries matching the query filters.
* You can read more about parameters
* [here](https://strapi.io/documentation/developer-docs/latest/content-api/parameters.html).
* */
find<T = any, Entities = string>(entity: Entities, params?: NuxtStrapiQueryParams<T> ): Promise<T>;

/**
* Count entries.
* Returns the count of entries matching the query filters.
* You can read more about parameters
* [here](https://strapi.io/documentation/developer-docs/latest/content-api/parameters.html).
* */
count<T = any, Entities = string>(entity: Entities, params?: NuxtStrapiQueryParams<T>): Promise<number>;

/**
* Get an entry by id and returns its value.
* */
findOne<T = any, Entities = string>(entity: Entities, id?: string): Promise<T>;

/**
* Creates an entry and returns its value.
* */
create<T = any, Entities = string>(entity: Entities, data?: NuxtStrapiQueryParams<T>): Promise<T>;

/**
* Partially updates an entry by id and returns its value.
* Fields that aren't sent in the query are not changed in the db.
* Send a null value if you want to clear them.
* */
update<T = any, Entities = string>(entity: Entities, id: string, data?: NuxtStrapiQueryParams<T>): Promise<T>;

/**
* Deletes an entry by id and returns its value.
* */
delete<T = any, Entities = string>(entity: Entities, id: string): Promise<T>;

/**
* Performs an HTTP request to GraphQL API and returns the requested data.
* */
graphql<T = any>(data: NuxtStrapiGraphQLParams): Promise<T>;

/**
* Register using local strategy. Sets the User and Token.
* */
register(data: NuxtStrapiRegistrationData): Promise<void>;

/**
* Login using local strategy. Sets the User and Token.
* */
login(data: NuxtStrapiLoginData): Promise<void>;

/**
* Send a request to the forgot-password endpoint of the server.
* */
forgotPassword(data: NuxtStrapiEmailData): Promise<void>;

/**
* Send a request to the reset-password endpoint of the server.
* */
resetPassword(data: NuxtStrapiResetPasswordData): Promise<void>;

/**
* Send an email confirmation for the login.
* */
sendEmailConfirmation(data: NuxtStrapiEmailData): Promise<void>;

/**
* Clears the user and jwt in cookies.
* */
logout(): void;

/**
* Fetch me user from /users/me route if a jwt is present in the cookies.
* Sets the jwt inside $http. Sets the User.
* */
fetchUser<T = any>(): Promise<T>;

/**
* This method fully overrides the user object, avoid using it.
* You can use the $strapi.user property to mutate single
* object properties instead of overriding it completely.
* */
setUser<T = any>(user: T): void;

/**
* Returns jwt from cookies.
* */
getToken(): string;

/**
* Sets token inside $http as a jwt Bearer.
* Store jwt in cookies.
* */
setToken(token: string): void;

/**
* Remove jwt from $http and $cookies.
* */
clearToken(): void;

$http: NuxtHTTPInstance;

$cookies: NuxtCookies;
}

declare module 'vue/types/vue' {
interface Vue {
$strapi: NuxtStrapi;
}
}

LuckeeDev marked this conversation as resolved.
Show resolved Hide resolved
declare module 'vuex/types/index' {
interface Store<S> {
$strapi: NuxtStrapi;
}
}

declare module '@nuxt/types' {
interface NuxtAppOptions {
$strapi: NuxtStrapi;
}

interface Context {
$strapi: NuxtStrapi;
}
}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"lib"
],
"main": "lib/module.js",
"types": "lib/index.d.ts",
"scripts": {
"dev": "nuxt example",
"docs": "nuxt docs",
Expand All @@ -34,6 +35,7 @@
"@babel/preset-env": "^7.12.11",
"@commitlint/cli": "^11.0.0",
"@commitlint/config-conventional": "^11.0.0",
"@nuxt/types": "^2.14.12",
"@nuxtjs/eslint-config": "^5.0.0",
"@nuxtjs/module-test-utils": "latest",
"babel-eslint": "latest",
Expand Down
Loading