2
\$\begingroup\$

This is a very simple web API for a school assignment, as a part of a bigger system. The web API returns a list of courses or a single course as json. I doubt that I will get any feedback on the code since Java is taught in the course so I am posting it here.

I am a beginner in Racket but have experience with C-style languages (C, C#, Python, etc).

  • I want to know how I can improve my solution and to be more idiomatic?
  • My function for converting the row-result from the database query to json (rows-result->json) is very messy, how can improve this? I have added example results (mock-row-result) from the database query that can be used as an argument.

The web api has two end points:

  • GET /course/{courseid}
  • GET /course with optional parameters: CourseCode, StudyPeriod

The database is a single table in an sqlite-database with the rows:

  • id - int
  • AppCode - text
  • CourseCode - text
  • StudyPeriod - text
  • Name - text

Code:

#lang racket
(require web-server/servlet
         web-server/servlet-env
         web-server/dispatch
         db
         json
         net/url-structs)

(define PORT 8081)
(define DATABASE "paraplyet.db")

; Database connection
(define paraplyet-db
  (sqlite3-connect #:database DATABASE))

; Convert results from database to json, messy
(define (rows-result->json result)
  (define column-names
    (map (lambda (r) (string->symbol (cdr (first r)))) (rows-result-headers result)))
  (define rows-lists (map vector->list (rows-result-rows result)))
  ; Create a list of hashmaps to be able to use jsexpr->string
  (define result-hashmaps
    (map (lambda (row) (make-hash (map cons column-names row))) rows-lists))
  (jsexpr->string result-hashmaps))

; Mock results from database query, can be used for the function rows-result->json
(define mock-row-result (rows-result
                         '(((name . "AppCode") (decltype . "TEXT"))
                           ((name . "CourseCode") (decltype . "TEXT"))
                           ((name . "StudyPeriod") (decltype . "TEXT"))
                           ((name . "Name") (decltype . "TEXT")))
                         '(#("LTU-37067" "D0004N" "HT2018" "Databaser I")
                           #("LTU-17045" "D0020N" "HT2018" "Utveckling av informationssystem"))))    


; Return list of courses with optional filtering the the parameters CourseCode and StudyPeriod
(define (get-courses req)
  (define params (make-hash (url-query (request-uri req))))
  (define courses (query paraplyet-db "SELECT AppCode, CourseCode, StudyPeriod, Name
                                       FROM Course
                                       WHERE CourseCode = UPPER(COALESCE($1, CourseCode)) AND
                                             StudyPeriod = UPPER(COALESCE($2, StudyPeriod))"
                         (hash-ref params 'CourseCode sql-null)
                         (hash-ref params 'StudyPeriod sql-null)))

  ; Respond with json
  (response/xexpr (rows-result->json courses)
                  #:mime-type (string->bytes/utf-8 "application/json")))

; Return a specific course
(define (get-course req app-code)
  (define courses (query paraplyet-db "SELECT AppCode, CourseCode, StudyPeriod, Name
                                       FROM Course
                                       WHERE AppCode = $1" app-code))

  ; Return 404 if course is missing, otherwise return JSON
  (if (empty? (rows-result-rows courses))
      (response/xexpr "404: Resource not found" #:code 404)
      (response/xexpr (rows-result->json courses)
                      #:mime-type (string->bytes/utf-8 "application/json"))))

; Function to respond with 404
(define (not-found req)
  (response/xexpr "404: Not Found"
                  #:code 404))


; Definition of routing
; /course                  Get many courses (filter with parameters)
; /course/{AppCode}        Get course with specific AppCode
(define-values (go _)
  (dispatch-rules
   [("course" (string-arg)) #:method "get" get-course]
   [("course")              #:method "get" get-courses]
   [else not-found]))

(define (start-server)
  (serve/servlet go
                 #:port PORT
                 #:servlet-regexp #rx""
                 #:launch-browser? #f
                 ))
(start-server)
\$\endgroup\$
  • \$\begingroup\$ Welcome to Code Review. Could you add some more information, e.g. your API end points and the scheme of the underlying tables? That will make it easier for reviewers to concentrate on your code, as they don't have to pry that information from the SQL statements. Thanks in advance, and I hope you get nice reviews. \$\endgroup\$ – Zeta Nov 9 '18 at 9:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.