├── .eslintignore ├── commitlint.config.js ├── lib ├── logger.js ├── module.js └── plugin.js ├── husky.config.js ├── .eslintrc.js ├── babel.config.js ├── .editorconfig ├── example ├── middleware │ └── settings-loader.js ├── pages │ ├── _.vue │ ├── de │ │ ├── _.vue │ │ └── index.vue │ ├── fr │ │ ├── _.vue │ │ └── index.vue │ ├── index.vue │ └── blog.vue ├── nuxt.config.js └── store │ └── storyBlok.js ├── jest.config.js ├── .github └── workflows │ └── lint-and-test.yml ├── test ├── getSettings.test.js ├── getStory.test.js ├── getCurrentStory.test.js └── getStoryCollection.test.js ├── LICENSE ├── .gitignore ├── package.json ├── CHANGELOG.md └── README.md /.eslintignore: -------------------------------------------------------------------------------- 1 | # Common 2 | node_modules 3 | dist 4 | .nuxt 5 | coverage 6 | 7 | # Plugin 8 | lib/plugin.js -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | '@commitlint/config-conventional' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /lib/logger.js: -------------------------------------------------------------------------------- 1 | const consola = require('consola') 2 | 3 | module.exports = consola.withScope('nuxt:storyblok-query') 4 | -------------------------------------------------------------------------------- /husky.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | hooks: { 3 | 'commit-msg': 'commitlint -E HUSKY_GIT_PARAMS', 4 | 'pre-commit': 'yarn lint', 5 | 'pre-push': 'yarn lint' 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | root: true, 4 | parserOptions: { 5 | parser: 'babel-eslint', 6 | sourceType: 'module' 7 | }, 8 | extends: [ 9 | '@nuxtjs' 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | '@babel/preset-env', { 5 | targets: { 6 | esmodules: true 7 | } 8 | } 9 | ] 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_size = 2 6 | indent_style = space 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /example/middleware/settings-loader.js: -------------------------------------------------------------------------------- 1 | export default async function ({ store, $storyblok }) { 2 | const lang = $storyblok.getCurrentLang() 3 | 4 | if (lang !== store.state.storyBlok.language) { 5 | store.commit('storyBlok/setLanguage', lang) 6 | await store.dispatch('storyBlok/loadSettings') 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /example/pages/_.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 19 | -------------------------------------------------------------------------------- /example/pages/de/_.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 19 | -------------------------------------------------------------------------------- /example/pages/de/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 19 | -------------------------------------------------------------------------------- /example/pages/fr/_.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 19 | -------------------------------------------------------------------------------- /example/pages/fr/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 19 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | collectCoverage: true, 4 | collectCoverageFrom: [ 5 | 'lib/**/*.js', 6 | '!lib/plugin.js' 7 | ], 8 | moduleNameMapper: { 9 | '^~/(.*)$': '/lib/$1', 10 | '^~~$': '', 11 | '^@@$': '', 12 | '^@/(.*)$': '/lib/$1' 13 | }, 14 | transform: { 15 | '^.+\\.js$': 'babel-jest' 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /example/pages/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 20 | -------------------------------------------------------------------------------- /example/nuxt.config.js: -------------------------------------------------------------------------------- 1 | const { resolve } = require('path') 2 | 3 | const accessToken = 'pdkyV4RUmoFzi3IpAwbTrQtt' 4 | 5 | module.exports = { 6 | rootDir: resolve(__dirname, '../'), 7 | buildDir: resolve(__dirname, '.nuxt'), 8 | srcDir: __dirname, 9 | render: { 10 | resourceHints: false 11 | }, 12 | env: { 13 | accessToken 14 | }, 15 | router: { 16 | middleware: ['settings-loader'] 17 | }, 18 | modules: [resolve(__dirname, '../')], 19 | storyblokQueries: { 20 | accessToken, 21 | defaultLanguage: 'en' 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/pages/blog.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 26 | -------------------------------------------------------------------------------- /example/store/storyBlok.js: -------------------------------------------------------------------------------- 1 | export const state = () => ({ 2 | language: '', 3 | settings: { 4 | meta: [] 5 | } 6 | }) 7 | 8 | export const getters = { 9 | language: store => store.language, 10 | settings: store => store.settings 11 | } 12 | 13 | export const mutations = { 14 | setLanguage(state, language) { 15 | state.language = language 16 | }, 17 | setSettings(state, settings) { 18 | state.settings = settings 19 | } 20 | } 21 | 22 | export const actions = { 23 | async loadSettings({ commit }) { 24 | const settings = await this.$storyblok.getCurrentSettings() 25 | 26 | commit('setSettings', settings.story.content) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.github/workflows/lint-and-test.yml: -------------------------------------------------------------------------------- 1 | name: "lint-and-test" 2 | 3 | on: ['push'] 4 | 5 | jobs: 6 | lint-and-test: 7 | name: "lint-and-test" 8 | runs-on: "ubuntu-latest" 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Setup node 12 13 | uses: actions/setup-node@v2 14 | with: 15 | node-version: 12.x 16 | - uses: actions/cache@v2 17 | with: 18 | path: '**/node_modules' 19 | key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }} 20 | 21 | - name: Install Node dependencies 22 | run: yarn install 23 | 24 | - name: "Lint" 25 | run: yarn run lint 26 | 27 | - name: "Unit Test" 28 | run: yarn run test:unit 29 | -------------------------------------------------------------------------------- /test/getSettings.test.js: -------------------------------------------------------------------------------- 1 | jest.setTimeout(60000) 2 | 3 | const { Nuxt, Builder } = require('nuxt') 4 | const request = require('request-promise-native') 5 | const getPort = require('get-port') 6 | 7 | const config = require('../example/nuxt.config') 8 | config.dev = false 9 | 10 | let nuxt, port 11 | 12 | const url = path => `http://localhost:${port}${path}` 13 | const get = path => request(url(path)) 14 | const buildNuxt = async (config) => { 15 | nuxt = new Nuxt(config) 16 | await nuxt.ready() 17 | await new Builder(nuxt).build() 18 | port = await getPort() 19 | await nuxt.listen(port) 20 | } 21 | 22 | describe('getSettings()', () => { 23 | beforeAll(async () => { 24 | await buildNuxt(config) 25 | }) 26 | 27 | afterAll(async () => { 28 | await nuxt.close() 29 | }) 30 | 31 | test('Fetch Settings in default language on Pageload', async () => { 32 | const html = await get('/') 33 | expect(html).toContain('__NUXT__') 34 | expect(html).toContain('og:title') 35 | expect(html).toContain('Storyblok Router') 36 | }) 37 | }) 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 WONDROUS – Digital Experience Company 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/getStory.test.js: -------------------------------------------------------------------------------- 1 | jest.setTimeout(60000) 2 | 3 | const { Nuxt, Builder } = require('nuxt') 4 | const request = require('request-promise-native') 5 | const getPort = require('get-port') 6 | 7 | const config = require('../example/nuxt.config') 8 | config.dev = false 9 | 10 | let nuxt, port 11 | 12 | const url = path => `http://localhost:${port}${path}` 13 | const get = path => request(url(path)) 14 | const buildNuxt = async (config) => { 15 | nuxt = new Nuxt(config) 16 | await nuxt.ready() 17 | await new Builder(nuxt).build() 18 | port = await getPort() 19 | await nuxt.listen(port) 20 | } 21 | 22 | describe('getStory()', () => { 23 | beforeAll(async () => { 24 | await buildNuxt(config) 25 | }) 26 | 27 | afterAll(async () => { 28 | await nuxt.close() 29 | }) 30 | 31 | test('Render /', async () => { 32 | const html = await get('/') 33 | expect(html).toContain('Hello server') 34 | }) 35 | 36 | test('Render / in different languages', async () => { 37 | const htmlFr = await get('/fr') 38 | expect(htmlFr).toContain('Hello French Server') 39 | 40 | const htmlDe = await get('/de') 41 | expect(htmlDe).toContain('Hello German Server') 42 | }) 43 | }) 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/code,nuxt,test,macos,nuxtjs 3 | # Edit at https://www.gitignore.io/?templates=code,nuxt,test,macos,nuxtjs 4 | 5 | ### Code ### 6 | .vscode/* 7 | !.vscode/settings.json 8 | !.vscode/tasks.json 9 | !.vscode/launch.json 10 | !.vscode/extensions.json 11 | 12 | ### macOS ### 13 | # General 14 | .DS_Store 15 | .AppleDouble 16 | .LSOverride 17 | 18 | # Icon must end with two \r 19 | Icon 20 | 21 | # Thumbnails 22 | ._* 23 | 24 | # Files that might appear in the root of a volume 25 | .DocumentRevisions-V100 26 | .fseventsd 27 | .Spotlight-V100 28 | .TemporaryItems 29 | .Trashes 30 | .VolumeIcon.icns 31 | .com.apple.timemachine.donotpresent 32 | 33 | # Directories potentially created on remote AFP share 34 | .AppleDB 35 | .AppleDesktop 36 | Network Trash Folder 37 | Temporary Items 38 | .apdisk 39 | 40 | ### Nuxt ### 41 | # gitignore template for Nuxt.js projects 42 | # 43 | # Recommended template: Node.gitignore 44 | 45 | # Nuxt build 46 | .nuxt 47 | 48 | # Nuxt generate 49 | dist 50 | 51 | # Nuxt Module 52 | coverage 53 | 54 | ### Nuxtjs ### 55 | # dependencies 56 | node_modules 57 | 58 | # logs 59 | npm-debug.log 60 | 61 | ### inteliJ ### 62 | .idea 63 | -------------------------------------------------------------------------------- /test/getCurrentStory.test.js: -------------------------------------------------------------------------------- 1 | jest.setTimeout(60000) 2 | 3 | const { Nuxt, Builder } = require('nuxt') 4 | const request = require('request-promise-native') 5 | const getPort = require('get-port') 6 | 7 | const config = require('../example/nuxt.config') 8 | config.dev = false 9 | 10 | let nuxt, port 11 | 12 | const url = path => `http://localhost:${port}${path}` 13 | const get = path => request(url(path)) 14 | const buildNuxt = async (config) => { 15 | nuxt = new Nuxt(config) 16 | await nuxt.ready() 17 | await new Builder(nuxt).build() 18 | port = await getPort() 19 | await nuxt.listen(port) 20 | } 21 | 22 | describe('getCurrentStory()', () => { 23 | beforeAll(async () => { 24 | await buildNuxt(config) 25 | }) 26 | 27 | afterAll(async () => { 28 | await nuxt.close() 29 | }) 30 | 31 | test('Render /about', async () => { 32 | const html = await get('/about') 33 | expect(html).toContain('About') 34 | }) 35 | 36 | test('Render /about in different languages', async () => { 37 | const htmlFr = await get('/fr/about') 38 | expect(htmlFr).toContain('About') 39 | expect(htmlFr).toContain('French') 40 | 41 | const htmlDe = await get('/de/about') 42 | expect(htmlDe).toContain('About') 43 | expect(htmlDe).toContain('German') 44 | }) 45 | }) 46 | -------------------------------------------------------------------------------- /lib/module.js: -------------------------------------------------------------------------------- 1 | const { resolve } = require('path') 2 | const StoryblokClient = require('storyblok-js-client') 3 | const logger = require('./logger') 4 | 5 | export default async function storyblokQueryModule (moduleOptions) { 6 | const defaultOptions = { 7 | accessToken: '', 8 | version: 'published', 9 | defaultLanguage: '', 10 | cacheProvider: 'memory' 11 | } 12 | 13 | const options = Object.assign( 14 | defaultOptions, 15 | this.options.storyblok, 16 | this.options.storyblokQueries, 17 | moduleOptions 18 | ) 19 | 20 | // Check if accessToken is defined 21 | if (!options.accessToken) { 22 | logger.warn('No Access Token found in Module Options') 23 | return 24 | } 25 | 26 | const client = new StoryblokClient({ 27 | accessToken: options.accessToken 28 | }) 29 | 30 | const { data: spaceData } = await client.get('cdn/spaces/me') 31 | 32 | const { language_codes: languageCodes = [] } = spaceData.space 33 | if (languageCodes.length) { 34 | // Check if defaultLanguage is defined 35 | if (!options.defaultLanguage) { 36 | logger.warn('No Default Language found in Module Options') 37 | return 38 | } 39 | 40 | languageCodes.unshift(options.defaultLanguage) 41 | options.languages = languageCodes 42 | } 43 | 44 | this.addPlugin({ 45 | src: resolve(__dirname, 'plugin.js'), 46 | options 47 | }) 48 | } 49 | 50 | module.exports.meta = require('../package.json') 51 | -------------------------------------------------------------------------------- /test/getStoryCollection.test.js: -------------------------------------------------------------------------------- 1 | jest.setTimeout(60000) 2 | 3 | const { Nuxt, Builder } = require('nuxt') 4 | const request = require('request-promise-native') 5 | const getPort = require('get-port') 6 | 7 | const config = require('../example/nuxt.config') 8 | config.dev = false 9 | 10 | let nuxt, port 11 | 12 | const url = path => `http://localhost:${port}${path}` 13 | const get = path => request(url(path)) 14 | const buildNuxt = async (config) => { 15 | nuxt = new Nuxt(config) 16 | await nuxt.ready() 17 | await new Builder(nuxt).build() 18 | port = await getPort() 19 | await nuxt.listen(port) 20 | } 21 | 22 | describe('getStoryCollection()', () => { 23 | beforeAll(async () => { 24 | await buildNuxt(config) 25 | }) 26 | 27 | afterAll(async () => { 28 | await nuxt.close() 29 | }) 30 | 31 | test('Render /blog', async () => { 32 | const html = await get('/blog') 33 | expect(html).toContain('

Blog

') 34 | }) 35 | 36 | test('Render /blog with blog posts', async () => { 37 | const html = await get('/blog') 38 | 39 | expect(html).toContain('

Post One

') 40 | expect(html).toContain('

Post Two

') 41 | expect(html).not.toContain('

Blog Posts

') 42 | }) 43 | 44 | test('Render /blog with blog posts and Startpage', async () => { 45 | const html = await get('/blog?startpage=1') 46 | 47 | expect(html).toContain('

Post One

') 48 | expect(html).toContain('

Post Two

') 49 | expect(html).toContain('

Blog Posts

') 50 | }) 51 | }) 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@wearewondrous/nuxt-storyblok-queries", 3 | "version": "1.2.14", 4 | "description": "Nuxt.js module to simplify queries to the Storyblok API", 5 | "main": "lib/module.js", 6 | "license": "MIT", 7 | "repository": "https://github.com/wearewondrous/nuxt-storyblok-queries", 8 | "contributors": [ 9 | { 10 | "name": "Serafin Lichtenhahn ", 11 | "url": "https://github.com/seralichtenhahn" 12 | } 13 | ], 14 | "scripts": { 15 | "dev": "nuxt example", 16 | "lint": "eslint --ext .js,.vue lib", 17 | "test": "yarn lint && yarn test:unit", 18 | "test:unit": "jest", 19 | "release": "yarn test && standard-version && git push --follow-tags && yarn publish" 20 | }, 21 | "dependencies": { 22 | "axios": "^0.21.1", 23 | "consola": "^2.15.3", 24 | "storyblok-js-client": "^3.3.1" 25 | }, 26 | "devDependencies": { 27 | "@babel/core": "^7.13.15", 28 | "@babel/preset-env": "^7.13.15", 29 | "@babel/runtime": "^7.13.10", 30 | "@commitlint/cli": "^11.0.0", 31 | "@commitlint/config-conventional": "^11.0.0", 32 | "@nuxtjs/eslint-config": "^5.0.0", 33 | "babel-eslint": "^10.1.0", 34 | "babel-jest": "^26.6.3", 35 | "codecov": "^3.8.1", 36 | "eslint": "^7.23.0", 37 | "eslint-config-standard": "^16.0.2", 38 | "eslint-plugin-import": "^2.22.1", 39 | "eslint-plugin-jest": "^24.3.4", 40 | "eslint-plugin-node": "^11.1.0", 41 | "eslint-plugin-promise": "^4.3.1", 42 | "eslint-plugin-standard": "^4.1.0", 43 | "eslint-plugin-vue": "^7.8.0", 44 | "get-port": "^5.1.1", 45 | "husky": "^4.3.8", 46 | "jest": "^26.6.3", 47 | "nuxt": "^2.15.4", 48 | "request-promise-native": "^1.0.9", 49 | "standard-version": "^9.2.0" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/plugin.js: -------------------------------------------------------------------------------- 1 | import StoryblokClient from "storyblok-js-client" 2 | 3 | function removeLeadingSlash(str) { 4 | return str.replace(/^\/+/g, "") 5 | } 6 | 7 | function addTrailingSlash(str) { 8 | return str.replace(/\/?(\?|#|$)/, '/$1') 9 | } 10 | 11 | class StoryblokQueries { 12 | constructor(ctx) { 13 | this.ctx = ctx 14 | this.languages = <%= JSON.stringify(options.languages || []) %> 15 | this.version = "<%= options.version %>" 16 | this.client = new StoryblokClient({ 17 | accessToken: "<%= options.accessToken %>", 18 | cache: { 19 | clear: "auto", 20 | type: "<%= options.cacheProvider || 'memory' %>" 21 | }, 22 | }) 23 | } 24 | 25 | get hasLanguages() { 26 | return this.languages.length > 0 27 | } 28 | 29 | getStory(path, options = {}) { 30 | if (this.hasLanguages && !options.lang) { 31 | options.lang = this.getCurrentLang() 32 | } 33 | 34 | path = this._getPath(path, options) 35 | return this._loadData({ 36 | path, 37 | options 38 | }) 39 | } 40 | 41 | getCurrentStory(options = {}) { 42 | const { 43 | route 44 | } = this.ctx 45 | 46 | if (this.hasLanguages && !options.lang) { 47 | options.lang = this.getCurrentLang() 48 | } 49 | 50 | let path = route.name === "index" ? "home" : route.path 51 | path = this._getPath(path, options) 52 | 53 | return this._loadData({ 54 | path, 55 | options 56 | }) 57 | } 58 | 59 | getSettings(lang = "", options = {}) { 60 | const path = this._getPath(`${lang}/${options.path || "settings"}`) 61 | return this._loadData({ 62 | path 63 | }) 64 | } 65 | 66 | getCurrentSettings(options = {}) { 67 | const lang = this.getCurrentLang() 68 | return this.getSettings(lang, options) 69 | } 70 | 71 | getStoryCollection(path, options = {}) { 72 | const lang = this.getCurrentLang() 73 | let startsWith = this._getPath(`${lang}/${path}`, options) 74 | if (!lang) { 75 | startsWith = this._getPath(`${path}`, options) 76 | } 77 | const startpage = options.startpage || false 78 | 79 | return this._loadData({ 80 | startsWith, 81 | startpage, 82 | options 83 | }) 84 | } 85 | 86 | getDatasource(path, options = {}) { 87 | const datatype = "datasource_entries" 88 | const datasource = path 89 | return this._loadData({ 90 | datatype, 91 | datasource, 92 | options 93 | }) 94 | } 95 | 96 | getCurrentLang() { 97 | return this._getLang(this.ctx.route.path) 98 | } 99 | 100 | getMode() { 101 | if (this.version !== "auto") { 102 | return this.version 103 | } 104 | 105 | let mode = "published" 106 | if (typeof window !== "undefined") { 107 | if (window.location === window.parent.location) { 108 | window.localStorage.removeItem("_storyblok_draft_mode") 109 | } 110 | if (this.ctx.query._storyblok || window.localStorage.getItem("_storyblok_draft_mode")) { 111 | window.localStorage.setItem("_storyblok_draft_mode", "1") 112 | mode = "draft" 113 | } 114 | } 115 | 116 | return mode 117 | } 118 | 119 | renderRichText(richTextContent) { 120 | if (richTextContent) { 121 | return this.client.richTextResolver.render(richTextContent) 122 | } 123 | } 124 | 125 | _loadData({ 126 | datatype = "stories", 127 | path = "", 128 | startsWith, 129 | startpage = true, 130 | datasource = "", 131 | options = {} 132 | }) { 133 | options = { 134 | ...options, 135 | version: this.getMode() 136 | } 137 | 138 | if (!startpage) { 139 | options.is_startpage = 0 140 | } 141 | 142 | if (startsWith) { 143 | options.starts_with = addTrailingSlash(startsWith) 144 | } 145 | 146 | if (datasource) { 147 | options.datasource = datasource 148 | } 149 | 150 | return this.client 151 | .get(`cdn/${datatype}/${path}`, options) 152 | .then(res => { 153 | return { 154 | ...res.data, 155 | ...res.perPage && { 156 | perPage: res.perPage 157 | }, 158 | ...res.total && { 159 | total: res.total 160 | } 161 | } 162 | }) 163 | .catch(res => { 164 | if (!res.response) { 165 | console.error(res) 166 | this.ctx.error({ 167 | statusCode: 404, 168 | message: "Failed to receive content from api" 169 | }) 170 | } else { 171 | console.error(`Request for : ${res.response.config.url}`) 172 | console.error(res.response.data) 173 | this.ctx.error({ 174 | statusCode: res.response.status, 175 | message: res.response.data 176 | }) 177 | } 178 | }) 179 | } 180 | 181 | _getPath(path, options) { 182 | const lang = this._getLang(path, options) 183 | const langPrefix = lang && lang !== this.languages[0] ? `${lang}/` : "" 184 | 185 | if (lang) { 186 | path = this._extractLangFromPath(path) 187 | } 188 | 189 | return `${langPrefix}${path}` 190 | } 191 | 192 | _getLang(path, options = {}) { 193 | if (!this.hasLanguages) { 194 | return "" 195 | } 196 | 197 | if (options.lang) { 198 | return options.lang 199 | } 200 | 201 | const normalizedPath = addTrailingSlash( 202 | removeLeadingSlash(path) 203 | ) 204 | 205 | const lang = this.languages.find((lang) => { 206 | return normalizedPath.indexOf(`${lang}/`) === 0 207 | }) 208 | 209 | return lang ? lang : this.languages[0] 210 | } 211 | 212 | _extractLangFromPath(path) { 213 | const lang = this._getLang(path) 214 | const normalizedPath = removeLeadingSlash(path) 215 | 216 | if (lang && normalizedPath.indexOf(lang) === 0) { 217 | return normalizedPath.replace(new RegExp(`${lang}/`, "i"), "") 218 | } 219 | 220 | return normalizedPath 221 | } 222 | } 223 | 224 | export default (ctx, inject) => { 225 | const storyblokContentDeliveryAPI = new StoryblokQueries(ctx) 226 | 227 | ctx.$storyblok = storyblokContentDeliveryAPI 228 | inject("storyblok", storyblokContentDeliveryAPI) 229 | } 230 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [1.2.14](https://github.com/wearewondrous/nuxt-storyblok-queries/compare/v1.2.13...v1.2.14) (2021-04-09) 6 | 7 | 8 | ### Bug Fixes 9 | 10 | * storyblok client import in plugin ([937bc60](https://github.com/wearewondrous/nuxt-storyblok-queries/commit/937bc602cdbb81264586a0e24117de3f2a61b4a8)) 11 | 12 | ### [1.2.13](https://github.com/wearewondrous/nuxt-storyblok-queries/compare/v1.2.12...v1.2.13) (2021-04-09) 13 | 14 | 15 | ### Bug Fixes 16 | 17 | * storyblok client import in plugin ([989991a](https://github.com/wearewondrous/nuxt-storyblok-queries/commit/989991a1e13696235f6548f8cd482e1e56d39b3e)) 18 | 19 | ### [1.2.12](https://github.com/wearewondrous/nuxt-storyblok-queries/compare/v1.2.11...v1.2.12) (2021-04-09) 20 | 21 | ### [1.2.11](https://github.com/wearewondrous/nuxt-storyblok-queries/compare/v1.2.10...v1.2.11) (2021-04-09) 22 | 23 | ### [1.2.10](https://github.com/wearewondrous/nuxt-storyblok-queries/compare/v1.2.9...v1.2.10) (2021-04-09) 24 | 25 | 26 | ### Bug Fixes 27 | 28 | * load right mode in production ([d70430e](https://github.com/wearewondrous/nuxt-storyblok-queries/commit/d70430e21b72661665394723756b9c578fe5bfec)) 29 | 30 | ### [1.2.9](https://github.com/wearewondrous/nuxt-storyblok-queries/compare/v1.2.8...v1.2.9) (2020-11-23) 31 | 32 | ### [1.2.8](https://github.com/wearewondrous/nuxt-storyblok-queries/compare/v1.2.5...v1.2.8) (2020-11-18) 33 | 34 | 35 | ### Bug Fixes 36 | 37 | * add option param to getDatasource ([e6b6b1a](https://github.com/wearewondrous/nuxt-storyblok-queries/commit/e6b6b1a02526407e0aed6f812c0f911c6533a1af)) 38 | 39 | ### [1.2.7](https://github.com/wearewondrous/nuxt-storyblok-queries/compare/v1.2.5...v1.2.7) (2020-11-18) 40 | 41 | 42 | ### Bug Fixes 43 | 44 | * add option param to getDatasource ([e6b6b1a](https://github.com/wearewondrous/nuxt-storyblok-queries/commit/e6b6b1a02526407e0aed6f812c0f911c6533a1af)) 45 | 46 | ### [1.2.6](https://github.com/wearewondrous/nuxt-storyblok-queries/compare/v1.2.5...v1.2.6) (2020-10-08) 47 | 48 | 49 | ### Bug Fixes 50 | 51 | * add option param to getDatasource ([e6b6b1a](https://github.com/wearewondrous/nuxt-storyblok-queries/commit/e6b6b1a02526407e0aed6f812c0f911c6533a1af)) 52 | 53 | ### [1.2.5](https://github.com/wearewondrous/nuxt-storyblok-queries/compare/v1.2.4...v1.2.5) (2020-08-25) 54 | 55 | ### [1.2.4](https://github.com/wearewondrous/nuxt-storyblok-queries/compare/v1.2.3...v1.2.4) (2020-08-25) 56 | 57 | ### [1.2.3](https://github.com/wearewondrous/nuxt-storyblok-queries/compare/v1.2.2...v1.2.3) (2020-02-27) 58 | 59 | ### [1.2.2](https://github.com/wearewondrous/nuxt-storyblok-queries/compare/v1.2.1...v1.2.2) (2020-02-27) 60 | 61 | ### [1.2.1](https://github.com/wearewondrous/nuxt-storyblok-queries/compare/v1.1.0...v1.2.1) (2020-01-31) 62 | 63 | 64 | ### Bug Fixes 65 | 66 | * correctly pass options when getting story collection ([c6e48e2](https://github.com/wearewondrous/nuxt-storyblok-queries/commit/c6e48e2)) 67 | * update README ([c8d52d0](https://github.com/wearewondrous/nuxt-storyblok-queries/commit/c8d52d0)) 68 | 69 | 70 | 71 | ## 1.2.0 (2020-01-14) 72 | 73 | 74 | 75 | ## [1.1.0](https://github.com/wearewondrous/nuxt-storyblok-queries/compare/v1.0.3...v1.1.0) (2020-01-14) 76 | 77 | 78 | ### Features 79 | 80 | * add rich text field renderer ([ed21119](https://github.com/wearewondrous/nuxt-storyblok-queries/commit/ed21119)) 81 | 82 | 83 | 84 | ### [1.0.3](https://github.com/wearewondrous/nuxt-storyblok-queries/compare/v1.0.2...v1.0.3) (2019-08-14) 85 | 86 | 87 | 88 | ### [1.0.2](https://github.com/wearewondrous/nuxt-storyblok-queries/compare/v1.0.1...v1.0.2) (2019-07-12) 89 | 90 | 91 | 92 | ### [1.0.1](https://github.com/wearewondrous/nuxt-storyblok-queries/compare/v1.0.0...v1.0.1) (2019-07-10) 93 | 94 | 95 | 96 | ## [1.0.0](https://github.com/wearewondrous/nuxt-storyblok-queries/compare/v0.1.9...v1.0.0) (2019-07-10) 97 | 98 | 99 | ### Bug Fixes 100 | 101 | * use correct language in getStory() ([cc831a5](https://github.com/wearewondrous/nuxt-storyblok-queries/commit/cc831a5)) 102 | 103 | 104 | ### Tests 105 | 106 | * add more unit tests ([da2da18](https://github.com/wearewondrous/nuxt-storyblok-queries/commit/da2da18)) 107 | 108 | 109 | 110 | ### [0.1.9](https://github.com/wearewondrous/nuxt-storyblok-queries/compare/v0.1.8...v0.1.9) (2019-06-14) 111 | 112 | 113 | ### Tests 114 | 115 | * add basic unit tests ([e02bb13](https://github.com/wearewondrous/nuxt-storyblok-queries/commit/e02bb13)) 116 | 117 | 118 | 119 | ### [0.1.8](https://github.com/wearewondrous/nuxt-storyblok-queries/compare/v0.1.7...v0.1.8) (2019-06-12) 120 | 121 | 122 | 123 | ### [0.1.7](https://github.com/wearewondrous/nuxt-storyblok-queries/compare/v0.1.6...v0.1.7) (2019-06-12) 124 | 125 | 126 | 127 | ### [0.1.6](https://github.com/wearewondrous/nuxt-storyblok-queries/compare/v0.1.5...v0.1.6) (2019-06-11) 128 | 129 | 130 | ### Bug Fixes 131 | 132 | * detect content version automatically ([f9a257d](https://github.com/wearewondrous/nuxt-storyblok-queries/commit/f9a257d)) 133 | * remove ENV Variable from if statement ([fd2d9ce](https://github.com/wearewondrous/nuxt-storyblok-queries/commit/fd2d9ce)) 134 | 135 | 136 | 137 | ### [0.1.5](https://github.com/wearewondrous/nuxt-storyblok-queries/compare/v0.1.4...v0.1.5) (2019-06-07) 138 | 139 | 140 | ### Bug Fixes 141 | 142 | * change regex to remove trailing slash ([441e4e3](https://github.com/wearewondrous/nuxt-storyblok-queries/commit/441e4e3)) 143 | 144 | 145 | 146 | ### [0.1.4](https://github.com/wearewondrous/nuxt-storyblok-queries/compare/v0.1.3...v0.1.4) (2019-06-07) 147 | 148 | 149 | 150 | ### [0.1.3](https://github.com/wearewondrous/nuxt-storyblok-queries/compare/v0.1.2...v0.1.3) (2019-06-06) 151 | 152 | 153 | 154 | ### [0.1.2](https://github.com/wearewondrous/nuxt-storyblok-queries/compare/v0.1.1...v0.1.2) (2019-06-06) 155 | 156 | 157 | 158 | ### 0.1.1 (2019-06-06) 159 | 160 | 161 | ### Bug Fixes 162 | 163 | * remove options from parameters on getDatasource ([153fbee](https://github.com/wearewondrous/nuxt-storyblok-queries/commit/153fbee)) 164 | * rename options key to storyblokQueries ([8dabb1d](https://github.com/wearewondrous/nuxt-storyblok-queries/commit/8dabb1d)) 165 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nuxt Storyblok Queries 2 | 3 | [![NPM](https://img.shields.io/npm/v/@wearewondrous/nuxt-storyblok-queries.svg)](https://www.npmjs.com/package/@wearewondrous/nuxt-storyblok-queries) 4 | [![CircleCI](https://circleci.com/gh/wearewondrous/nuxt-storyblok-queries.svg?style=shield&circle-token=53485e7e4fa60a611464761450c6230f5bafe9ff)](https://circleci.com/gh/wearewondrous/nuxt-storyblok-queries) 5 | [![Standard JS][standard-js-src]][standard-js-href] 6 | 7 | > Nuxt.js module to simplify queries to the Storyblok API 8 | 9 | [📖 **Release Notes**](./CHANGELOG.md) 10 | 11 | ## Setup 12 | 13 | 1. Add the `@wearewondrous/nuxt-storyblok-queries` dependency with `yarn` or `npm` to your project 14 | 2. Add `@wearewondrous/nuxt-storyblok-queries` to the `modules` section of `nuxt.config.js` 15 | 3. Configure it: 16 | 17 | ```js 18 | { 19 | modules: [ 20 | ['@wearewondrous/nuxt-storyblok-queries', { 21 | // Module options here 22 | }] 23 | ] 24 | } 25 | ``` 26 | 27 | ### Using top level options 28 | 29 | ```js 30 | { 31 | modules: [ 32 | '@wearewondrous/nuxt-storyblok-queries' 33 | ], 34 | storyblokQueries: [ 35 | // Module options here 36 | ] 37 | } 38 | ``` 39 | 40 | ## Options 41 | 42 | ### `accessToken` 43 | 44 | - Default: `this.options.storyblok || ''` 45 | 46 | Access Token for the StoryBlok API. Not needed if you already have installed the [Storyblok Nuxt.js module](https://github.com/storyblok/storyblok-nuxt) 47 | 48 | ### `cacheProvider` 49 | 50 | - Default: `'memory'` 51 | 52 | Cache Provider for the StoryBlok API. Not needed if you already have installed the [Storyblok Nuxt.js module](https://github.com/storyblok/storyblok-nuxt) 53 | 54 | ### `version` 55 | 56 | - Default: `'auto'` 57 | 58 | Version of the Storyblok Content. Use 'draft' together with the preview Access Token. 59 | 60 | ### `defaultLanguage` 61 | 62 | - Default: `''` 63 | 64 | Optional. If your Storyblok Site has multiple languages, set `defaultLanguage` to the key of your Storyblok default language. 65 | 66 | ## Usage 67 | 68 | This modules adds a simple API to query your Storyblok Content. 69 | 70 | * [$storyblok.getStory()](#storyblokgetstorypath-options) 71 | * [$storyblok.getCurrentStory()](#storyblokgetcurrentstoryoptions) 72 | * [$storyblok.getStoryCollection()](#storyblokgetstorycollectionpath-options) 73 | * [$storyblok.getSettings()](#storyblokgetsettingslang-options) 74 | * [$storyblok.getCurrentSettings()](#storyblokgetcurrentsettingsoptions) 75 | * [$storyblok.getDatasource()](#storyblokgetdatasourcepath) 76 | 77 | ### `$storyblok.getStory(path, options)` 78 | 79 | Fetches the story by the given path. The Language gets automatically detected or can be specified in the options parameter. 80 | 81 | ```js 82 | export default { 83 | async asyncData({ $storyblok }) { 84 | const story = await $storyblok.getStory("home") 85 | 86 | return story 87 | } 88 | } 89 | ``` 90 | 91 | #### with Options 92 | ```js 93 | export default { 94 | async asyncData({ $storyblok }) { 95 | const story = await $storyblok.getStory("home", { 96 | lang: "de" 97 | }) 98 | 99 | return story 100 | } 101 | } 102 | ``` 103 | 104 | ### `$storyblok.getCurrentStory(options)` 105 | 106 | Fetches the story by the current Route. The Language gets automatically detected but can also be specified in the options parameter. 107 | 108 | ```js 109 | export default { 110 | async asyncData({ $storyblok, route }) { 111 | console.log(route.path) // -> /story 112 | const story = await $storyblok.getCurrentStory() 113 | 114 | return story 115 | } 116 | } 117 | ``` 118 | 119 | #### with Options 120 | ```js 121 | export default { 122 | async asyncData({ $storyblok, route }) { 123 | console.log(route.path) // -> /story 124 | const story = await $storyblok.getCurrentStory({ 125 | lang: "de" 126 | }) 127 | 128 | return story 129 | } 130 | } 131 | ``` 132 | 133 | ### `$storyblok.getStoryCollection(path, options)` 134 | 135 | Fetches all Stories matching the given path. The Language gets automatically detected but can also be specified in the options parameter. 136 | 137 | ```js 138 | export default { 139 | async asyncData({ $storyblok, route }) { 140 | const collection = await $storyblok.getStoryCollection("blog") 141 | 142 | return collection 143 | } 144 | } 145 | ``` 146 | 147 | #### with Options 148 | ```js 149 | export default { 150 | async asyncData({ $storyblok, route }) { 151 | const collection = await $storyblok.getStoryCollection("blog", { 152 | lang: "de", 153 | startpage: true // if true, startpage of collection gets fetched as well 154 | }) 155 | 156 | return collection 157 | } 158 | } 159 | ``` 160 | 161 | 162 | ### `$storyblok.getSettings(lang, options)` 163 | 164 | Fetches the settings page of the given language. The path for the settings route can be specified in the options parameter or falls back to `/settings`. 165 | 166 | ```js 167 | export default { 168 | async asyncData({ $storyblok, route }) { 169 | const settings = await $storyblok.getSettings("de") 170 | 171 | return { 172 | //... 173 | settings 174 | } 175 | } 176 | } 177 | ``` 178 | 179 | #### with Options 180 | ```js 181 | export default { 182 | async asyncData({ $storyblok, route }) { 183 | const settings = await $storyblok.getSettings("de", { 184 | path: "global" 185 | }) 186 | 187 | return { 188 | //... 189 | settings 190 | } 191 | } 192 | } 193 | ``` 194 | 195 | 196 | ### `$storyblok.getCurrentSettings(options)` 197 | 198 | Fetches the settings page of the current language detected by the current route. The path for the settings route can be specified in the options parameter or falls back to `/settings`. 199 | 200 | ```js 201 | export default { 202 | async asyncData({ $storyblok, route }) { 203 | const settings = await $storyblok.getCurrentSettings() 204 | 205 | return { 206 | //... 207 | settings 208 | } 209 | } 210 | } 211 | ``` 212 | 213 | #### with Options 214 | ```js 215 | export default { 216 | async asyncData({ $storyblok, route }) { 217 | const settings = await $storyblok.getCurrentSettings({ 218 | path: "global" 219 | }) 220 | 221 | return { 222 | //... 223 | settings 224 | } 225 | } 226 | } 227 | ``` 228 | 229 | 230 | ### `$storyblok.getDatasource(path)` 231 | 232 | Fetches the datasource by the given path. 233 | 234 | ```js 235 | export default { 236 | async asyncData({ $storyblok, route }) { 237 | const datasource = await $storyblok.getDatasource("users") 238 | 239 | return { 240 | //... 241 | datasource 242 | } 243 | } 244 | } 245 | ``` 246 | 247 | 248 | ### `$storyblok.renderRichText(richTextContent)` 249 | 250 | Renders the Storyblok richtext field content and returns an HTML string. 251 | 252 | ```html 253 |
254 | ``` 255 | 256 | 257 | ## Development 258 | 259 | 1. Clone this repository 260 | 2. Install dependencies using `yarn install` or `npm install` 261 | 3. Start development server using `npm run dev` 262 | 263 | ## License 264 | 265 | [MIT License](./LICENSE) 266 | 267 | Copyright (c) [WONDROUS LTD](https://www.wearewondrous.com/) 268 | 269 | 270 | [standard-js-src]: https://img.shields.io/badge/code_style-standard-brightgreen.svg?style=flat-square 271 | [standard-js-href]: https://standardjs.com 272 | --------------------------------------------------------------------------------