Extension Review Guidelines
Backend Development
Error Codes
All error codes should describe the error itself and not the source of the error. The frontend should be able to decide what to do.
See also: General Error Codes
❌ Wrong
`ESOMEFEATURE`, `EBIGAPI`
✅ Right
`ETIMEOUT`, `EINVALIDNAME`
State
Do not save any state into the step file (except caching).
❌ Wrong
let persistentStorage = {}
module.exports = async (context, input) => {
persistentStorage['foo'] = 'bar'
}
✅ Right
module.exports = async (context, input) => {
await context.storage.extension.set('foo', 'bar')
}
Simplify Steps
Do not put all logic into one step. Instead, split it into smaller, logical parts.
Do not use console.log
Do not use any console output. The output is not forwarded to the log cluster and will be lost forever.
❌ Wrong
module.exports = async (context, input) => {
console.log('I am wrong')
}
✅ Right
module.exports = async (context, input) => {
context.log.debug('I am right')
}
Do not use get
function to pass output
Do not pass any object where the key needs to be fetched via a get
function. If you need to implement a get
function, also implement the toJSON
function.
❌ Wrong
class MyDTO {
constructor(value1, value2) {
this._value1 = value1
this._value2 = value2
}
get value1() {
return this._value1
}
get value2() {
return this._value2
}
}
module.exports = async (context, input) => {
const dto = new MyDTO('foo', 'bar')
return dto
}
✅ Right
module.exports = async (context, input) => {
return {value1: 'foo', value2: 'bar'}
}
✅ Right
class MyDTO {
constructor(value1, value2) {
this._value1 = value1
this._value2 = value2
}
get value1() {
return this._value1
}
get value2() {
return this._value2
}
toJSON() {
return {
value1: this._value1,
value2: this._value2
}
}
}
module.exports = async (context, input) => {
const dto = new MyDTO('foo', 'bar')
return dto
}
Do not return null
Always return an empty object.
❌ Wrong
module.exports = async (context, input) => {
return null
}
✅ Right
module.exports = async (context, input) => {
return {}
}
Do not return an error, throw it
Do not return an error. It will be handled as a normal object.
❌ Wrong
module.exports = async (context, input) => {
return new Error()
}
✅ Right
module.exports = async (context, input) => {
throw new Error()
}
Do not overuse storage
Keep the count of storage as low as possible. Every request takes time to get answered, which delays the response time of the actual pipeline request.
Store only the essentials.
❌ Wrong
module.exports = async (context, input) => {
const response = await request('https://awesomeDomain/', { resolveWithFullResponse: true })
await context.storage.device.set('foo', response)
}
✅ Right
module.exports = async (context, input) => {
const response = await request('https://awesomeDomain/', { resolveWithFullResponse: true })
await context.storage.device.set('foo', response.body.keyToStore)
}
Do not share a logger instance between requests
The logger instance given in the context includes the request ID to improve log tracing.
Storing the logger instance statically and reusing it in another request destroys the traceability.
❌ Wrong
let logger = null
module.exports = async (context, input) => {
logger = context.log
logger.info('Do not do this')
}
✅ Right
module.exports = async (context, input) => {
context.log.info('Do this')
}
Use automatic step insertion
Use automatic step insertion where possible. This approach makes it easier for merchants to install the app.
If a hook does not exist where needed, please provide Shopgate with this feedback.